JVM复习总结

久未见 提交于 2020-02-22 19:49:45

JVM即Java虚拟机,主要用来编译,执行Java代码,并在执行过程中对内存进行管理.

Java代码的执行流程

Java代码首先要通过javac前期编译器从.java文件编译为.class字节码文件(注解处理),然后将.class文件交由虚拟机的类加载器ClassLoader进行装载(采用双亲委派机制进行装载),将.class文件装入java虚拟机后,Hotspot虚拟机根据配置的模式(混合模式,解释模式,编译模式)来决定使用解释器直接解释执行还是使用JIT即时编译器进行编译执行,亦或者是两者混用的混合模式进行执行.默认采用混合模式执行.

  • Hotspot虚拟机内置了两个即时编译器,称为client编译器server编译器(C1,C2编译器),前者进行一些稳定可靠的优化,后者还会进行一些激进不可靠的优化策略.后者编译出的机器码相对前者质量要高,而前者进行编译的速度要比后者快.
  • 可靠的编译优化:热点代码替换,栈上替换.原理:基于采样的热点探测,方法调用计数器和回边计数器.  方法内联
  • 基于逃逸分析的编译优化:栈上分配,标量替换,同步消除.

java运行时数据区

程序运行后要对内存进行管理,Java虚拟机的运行时数据区分为,虚拟机栈,本地方法栈,程序计数器,元空间

中存放着对象(大小不固定),虚拟机栈中存放着对象的引用(4byte)和基本数据类型(大小固定)(堆栈分离的好处:对象可以多栈共享,采用面向对象的方式进行统一处理).

本地方法栈用来执行本地方法,虚拟机栈用来执行java方法.

程序计数器类似于字节码的行号指示器,用于取字节码指令.

元空间用于存放类的元数据信息(包括静态变量和字符串常量池(字符串的引用),方法代码,方法数据),元空间的大小受限于本机的虚拟内存大小.而虚拟内存大小受本地物理内存和地址指令位数的限制.

直接内存不属于Java运行时数据区的一部分,可以用NIO的通道和Buffer来直接操作这块内存,避免了java堆和native堆的来回复制数据,提高了性能.

栈中对象的引用和基本数据类型随着栈帧的出栈而销毁.而堆中的对象则由Java虚拟机的垃圾收集器进行检测回收.

如何确定哪些对象需要回收?

  • 引用计数法(简单,循环引用会导致无法回收,有内存泄漏风险).
  • 可达性分析(HotSpot采用,枚举根节点GC Roots(虚拟机栈中引用的对象,本地方法栈中引用的对象,static属性引用的对象,final属性引用的对象),会导致Stop The World)

什么时候回收?

到达安全点或者安全区域的时候.

如何到达安全点,主动式中断.

垃圾回收算法

常用的垃圾回收算法分为三大类,标记清除(简单,产生碎片,提取触发),复制(空间浪费),标记整理(无碎片,移动存活对象费时)

堆分为新生代和老年代,新生代分为Eden区和两个Survivor区,大小比例为8:1:1(防止空间浪费),新生代采用复制算法(使用老年代进行分配担保,GC分代年龄阈值为15,默认为1).老年代采用标记整理算法(回收质量低,速度慢,存活周期长).

动态对象年龄判定,幸存者区某年龄对象大小之和存活超过幸存者区一半大小,大于等于该年龄的对象直接晋升老年代.

空间分配担保,Minor GC前判断,老年代可用连续空间>新生代对象总大小|历次晋升对象大小的平均值,则进行Minor GC,否则进行Full GC.

Minor GC(新生代)、Major GC(老年代)和Full GC(新生代+老年代)

Java中的四种引用

强引用(不回收),软引用Soft Reference(下一次溢出前回收),弱引用Weak Reference(下一次直接回收),虚引用Phantom Reference(回收时通知)

垃圾收集器

serial(新生代)单线程,复制算法,client模式下默认.

parnew(新生代)多线程,收集线程数与cpu核心数量一致.

parallel scavenge(新生代)多线程,复制算法,关注吞吐量.GC自适应调节策略.

serial old(老年代)单线程,标记整理算法,client模式下与serial联用.

parallel old(老年代)多线程,标记整理算法,关注吞吐量.

CMS(老年代)多线程,标记清除,并发低停顿,CPU资源敏感(少于4个性能下降),会产生空间碎片,无法处理浮动垃圾,用serial old做后备预案.

G1(整个堆)整体标记整理,局部复制,划分区域,判断价值,追求低停顿.

垃圾收集器的搭配使用

serial+serial old

parallel scavenge+parallel old

Serial+CMS+Serial Old

ParNew+CMS+Serial Old

parnew+serial old

Parallel Scavenge+Serial old

G1

类的生命周期

加载>>连接(验证,准备,解析(词法分析,语法分析))>>初始化>>使用>>卸载.

准备阶段为类变量赋零值.final直接赋程序中的值.

初始化阶段执行类构造器给类变量赋值,还会初始化其他资源.

双亲委派模型

启动类加载器Bootstrap ClassLoader 加载/lib目录下的 获取方法:ClassLoader.getSystemClassLoader().getParent().getParent()

扩展类加载器Extension ClassLoader 加载/lib/ext目录下的 获取方法:ClassLoader.getSystemClassLoader().getParent()

系统类加载器Application ClassLoader 加载Classpath目录下的 获取方法:ClassLoader.getSystemClassLoader()

自定义类加载器Custom ClassLoader 定义方法:继承ClassLoader抽象类,重写findClass()方法.

双亲委派模型用于保持java程序的稳定运行.

通过线程上下文类加载器Thread Context ClassLoader可以破坏双亲委派模型.

破坏双亲委派模型的典型应用场景,代码热替换,模块热部署.原理:连同类加载器一同卸载.

重载与重写

静态单分派动态多分派,静态是编译阶段确定,动态是运行阶段确定.方法的返回类型参与重写不参与重载.宗量,方法的接收者与方法的参数.

java内存模型

java使用的是轻量级进程与内核线程一比一的模型.优点,不会阻塞整个进程,缺点,用户态与核心态切换,轻量级进程数量受限于内核资源.

内存分配的两种方式:

指针碰撞,内存规整,一个指针分界.适用于Serial收集器和ParNew收集器.

空闲列表,内存不规整,维护列表记录可用内存块,适用于CMS收集器.

线程安全

volatile语义:保持可见性,禁止JVM的指令重排序.

CAS比较并交换原理,原子操作,内存位置V,旧值A,新值B,CAS存在的ABA问题.

锁优化

自旋锁,适合锁定短时间.

自适应自旋锁.

锁消除,同步消除,依赖于逃逸分析(方法逃逸,线程逃逸).

锁粗化,扩大锁粒度.

虚拟机参数:

-version查看版本及运行模式

-Xmixed使用混合模式,启动时使用解释器解释执行,程序运行后使用编译器编译热点代码为本地机器码,如果运行在server模式下,还会进行激进优化,如果激进优化失败,则退回到解释执行.

-Xint使用解释模式,全部解释执行.

-Xcomp使用编译模式,优先编译执行,无法编译时使用解释器解释执行.

-client使用client模式,不会进行激进优化.

-server使用server模式,会进行激进优化.

-XX+printGCDetails打印GC日志详细信息

-Xms堆的初始大小

-Xmx堆的最大大小

-Xss线程空间大小

-Xmn新生代大小,通常为-Xmx的三分之一或者四分之一

-XX:UseStringCache默认开启,缓存常用的字符串

-XX:PrintGCDateStamps打印GC的耗时

-XX:MetaspaceSize初始元空间大小和元空间GC阈值

-XX:MaxTenuringThreshold分代年龄的阈值

-XX:PretenureSizeThreshold大对象直接进入老年代阈值

-XX:NewRatio新生代与老年代的比例

-XX:SurvivorRatio新生代Eden和Survior的比值,默认为8.

-XX:PermSize永久代初始大小,1.8后自动忽略

-XX:MaxPermSize永久代最大大小,1.8后自动忽略.

-XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时 Dump 出当前的内存堆转储快照,以便分析用

-XXHeapDumpPath=/home/admin/logs指定堆转储文件的路径.

-XX:+UseSerialGC使用指定收集器

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:+UseConcMarkSweepGC

-XX:+UseG1GC

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!