在JAVA中我们知道垃圾回收都是JVM自动去处理的,但有时候需要进行一些性能调优时,必须对JVM的垃圾收集有一定了解;
首先要知道JVM是如何在堆中分配对象,JVM是通过指针碰撞的方式寻找下一个存放地址,当如果下一个存放地址的大小不足以放下一个对象时,此时触发GC回收(正常情况是Minor GC);正常分配时在多线程环境中存在存储资源竞争,需要全局加锁,但是效率低下,所以JVM引入的TLAB(Thread Local Allocation Buffer),线程本地缓冲,该空间指定年轻代的EDEN区默认占比1%,可通过-XX:TLABWasteTargetPercent参数设置占比,可用-XX:+PrintTLAB查看占比,因为大多数对象较小所以用该分配方式效率较高。
垃圾收集算法种类:
1、复制算法:开辟两块大小一样的空间,我们称为from区和to区,当进行垃圾收集时,会将存活的对象复制至to区,如果to区不够存放存活对象时,可采用分配担保机制将不够存放的对象直接放入老年代,然后在将from区和其他区域清除,from区和to区角色互换;优点是速度快,不易产生内存碎片,缺点是每次都要开辟两块大小一样的空间,有点浪费,见下图:
标记复制算法2、标记清除算法:和复制不一样,当系统标记后之后,对所有不可用对象的空间直接清除,缺点是效率不高,容易产品内存碎片,见下图:
标记清除3、标记整理算法:类似标记清除类似,只是整理算法在做清除的时候是将所有可用的对象将一端移动,然后清理端以后不可用的对象,见下图:
标记整理可达性分析算法:
JVM中通过可达性分析来判断对象是否存活,当一个对象的引用没有执行GC roots时,那么我们认为该对象不可用,可以进行回收,见下图:
可达性分析算法上面介绍了一些垃圾收集算法和判断对象可用的算法,下面来介绍具体的垃圾收集器;
1、Serial收集器:年轻代收集,单线程收集,采用复制算法,默认Client模式默认收集器,对于单核CPU和堆内存小使用效率比较好,缺点是Stop-the-world,收集垃圾时所有用户线程不可用,JVM参数中用-XX:+UseSerialGC开启;
2、SerialOld收集器:老年代收集,单线程,采用标记整理算法,缺点和Serial收集器类似,JVM参数中用-XX:+UseSerialOldGC开启;
3、ParNew收集器:年轻代收集,多线程,采用复制算法,好处是速度快,可开启多个垃圾线程收集,优点是可并行运行,缺点单核CPU下效率不比Serial收集器快,JVM参数下用-XX:+UseParNewGC开启;
4、Parallel Scavenge收集器:年轻代收集,采用复制算法,可并行多线程收集器,优点是注重吞吐量,缺点是停顿时间较长,用户体验不太好;停顿时间可用-XX:MaxGCPauseMillis参数设置,吞吐量大小-XX:GCTimeRatio参数设置;例如设置吞吐量19,那最大GC时间就占总时间5%;(1/(1+19)),默认值99;该收集器提供一个参数-XX:+UseAdaptiveSizePolicy,该参数属于自适应调节策略,JVM会根据运行情况收集性能监控信息,动态调整这些参数(年轻大小-Xmn,Eden和Survivor去的占比-XX),JVM参数中用-XX:+UseParallelGC开启;
5、Parallel Old收集器:老年代收集,多线程,采用标记整理算法,优缺点和Parallel Scavenge收集器一样,只是之前老年代收集只有Serial Old收集,所以Parallel Scavenge收集和Serial Old收集组合一起发挥不了吞吐量的优势,所以出现了该收集器,JVM参数中用-XX:+UseParallelOldGC开启;
6、CMS收集器:老年代收集,多线程,采用标记清除算法,优点是可并行并发处理,注重停顿时间,用户体验更快,缺点是产生浮动垃圾,内存碎片,吞吐量会下降,CMS收集分四个步骤,分别是初始标记==>并发标记==>重新标记==>并发清除,其中初始标记和重新标记会出现Stop the World,而并发标记和并发清除可与用户线程一同运行;什么是浮动垃圾,当在做并发清除是,用户线程也在运行,那么势必会有新的垃圾收集,由于已经处在清除阶段了,所以只能下一次去清理掉,CMS收集默认不是老年代填满进行收集,而是预留一定的空间供收集是的程序使用,如果预留空间不足,那么就会出现Concurrent Mode Failure错误,当出现这个错误时,JVM提供一个预案:临时启动Serial Old收集器,JVM提供一个参数来社会自百分比:-XX:CMSInitiatingOccupancyFraction来设置,此参数最好不要设置过大,如果过大则可能出现 Concurrent Mode Failure错误,性能会下降;由于CMS收集会产生内存碎片,所以JVM提供了一个参数:-XX:+UseCMSCompactAtFullCollection设置,当内存不足需要进行GC是,可先开启内存:缩;JVM参数中可用-XX:+UseConcMarkSweepGC开启;
7、G1收集器:年轻代和老年代共用,多线程处理,采用标记整理算法,优点是可并行并发处理、分代收集,空间整合、有点回收垃圾多的区域、可预测低停顿;G1收集分四个步骤:初始标记==>并发标记==>最终标记==>筛选回收;G1收集做法是将堆划分大小一样的Region区域,针对具体的区域回收,G1通过Remembered Set方式避免全堆扫描,每个Region都有对应的Remembered Set,每次Reference类型数据写操作时,JVM产生一个Write Barries进行终端,检查Reference引用的对象是否处于不同的Region之间,如果是,通过卡表把相关引用信息记录到被引用对象的Remembered Set中,那么进行可达性分析时加入Remembered set记性扫描;JVM参数中可用-XX:+UseG1GC开启;
注:Parallel Scavenge和Parallel Old注重吞吐量,而CMS收集器和G1收集注重低停顿;
针对上面出现的一些词语进行解释:
吞吐量:举个例子,假如JVM运行100分钟,垃圾回收用1分钟,那么吞吐量就是99%,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间);
并行:这个就是在垃圾收集是,可以有多个垃圾收集线程同时运行,此时用户线程正在暂停;
并发:这个意思就是用户线程与垃圾收集线程同时运行,也有可能交替执行,具体看CPU核数;
网友评论