Jvm类的创建过程包括类的加载,类的验证,准备,分析,初始化。
验证是不是.class文件。
准备过程则是先赋值初始化的值,并不是直接赋值原始值。
分析比较复杂,会有静态链接处理和动态链接处理。
最后就是类的初始化。
前面还有一个类的加载没说,类的加载则需要考虑到双亲委派,有三个类自带的核心加载器,bootStrap加载器,扩展加载器,app加载器,后面则有自定义加载器。
前面则说了垃圾收集器有复制,标记整理,标记清除。Serial,parallel,ParNew。
垃圾收集器ParNew-JVM(十)
- CMS(Concurrent Mark Sweep)
cms收集器是一种考虑用户体验的收集器,以最短停顿为目的而设计的,它是第一个真正意义上实现用户线程与垃圾收集器并行工作。从mark sweep可以看出来它是标记清除算法,整体来说比前面介绍的都复杂点。
- 初始标记:暂停所有其他用户线程STW,并可达性算法记录GC roots直接能引用的对象,速度很快,用户基本无感知。
(因为速度必须要快,所以要STW,不然一直新增对象一直标记难以保证速率)
- 并发标记:并发标记就是开始遍历整个对象图的过程,这个过程耗时长,但不会暂停线程(不需要STW),垃圾线程和用户线程一起执行。
已经标记过的状态可能会发生变化,之前是垃圾变为对象,是对象的变为垃圾。
初始标记只标记一个直接引用对象,并发标记会从这个引用对象一直找,直到找完堆。
(为了用户体验,耗时长,所以不STW)
- 重新标记:重新标记主要是为了修改并发标记期间改变的对象状态,这个耗时会稍微长点,但远远比并发标记耗时短,这里主要用到三色标记里的增量更新算法。
- 并发清理:开启用户线程,同时GC线程对未标记的做清理。新增的对象则标记为黑色(三色标记)不做任何处理。
- 并发重置:重置本次GC过程的标记数据。
其中并发标记占用时间最长,约占收集过程中百分之80的时间。
整个STW只有初始标记和重新标记部分,这两部分耗时非常低,所以用户体验基本无感知。
可以看出来cms是一个优秀的垃圾收集器,优点是:并发收集、停顿低。但他也是有缺点的:
- 对CPU资源敏感,会和服务抢资源。
- 浮动垃圾无法清理。(前面说了在并发标记和并发清理阶段是和用户线程并行的,这时候有新的用户对象则不会清理,会等到下次GC再清理)
- 因为他使用的标记清除,所以导致会有大量碎片,必须和JVM参数一起用
-XX:UseCMSCompactAtFullCollection
可以让jvm在执行完标记清除后整理碎片空间。
- 执行过程又不确定性,当垃圾还没收集完,用户线程又有新的对象进入,特别是并发阶段并发标记和并发清理,触发fullGC,这时候会触发concurrent mode failure,此时会进入全部的STW,用Serail old垃圾收集器。
(ParNew并发处理年轻代,CMS并发处理老年代,内存不够用,于是就用serial old单线程运行)
那么如何避免垃圾收集器用serial old收集呢?
1、启动CMS参数:-XX:+UseConcMarkSweep
(jdk1.8是可以用的,1.9之后则会提示过时(可以用),默认用的G1)
- -XX:+ConcGCThreads:并发线程数
- -XX:+UseCMSCompactAtFullCollection:fullGC之后减少碎片,整理。
- -XX:+CMSFullGCsBeforeCompaction:多少次fullGC才会压缩整理碎片,默认是0。
- -XX:+CMSInitiatingOccupancyFraction:当老年代使用达到该比例才出发fullGC,默认是达到百分92才fullGC。
(这个参数就是解决执行的不确定性,防止concurrent mode failture,保证剩余空间继续收集,如果系统大对象太多,则考虑调整小,调整为90,80或者75)
- -XX:+UseCMSInitiatingOccupancyOnly:只使用设定的回收阀值,
与-XX:+CMSInitiatingOccupancyFraction连用,如果不指定,则只在第一次生效,后续则会自动调整,比较智能化。
- -XX:+CMSScavengeBeforeRemark:配置之后,会在fullGC前启动一次minor GC,会提高效率,先减少一部分垃圾对象。
- -XX:+CMSParallellnitialMarkEnabled:表示初始化也并行,缩短STW。
- -XX:+CMSParallelRemarkEnabled:重新标记的时候也是多线程并行,缩短STW。