尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

JVM垃圾回收器的工作原理,参数设置,性能调优

JVM垃圾回收器的工作原理,参数设置,性能调优 深入揭秘JVM的年轻代垃圾回收器ParNew是如何工作的最常用的新生代垃圾回收器ParNew一般来说很多年之前没有最新的G1垃圾回收器大家在线上系统通常是用ParNew作为新生代垃圾回收器即使现在有了G1还有很多系统用ParNew运行在服务器上的Java系统都可以充分利用服务器的多核心CPU的优势而为了适配这个ParNew也设计了多线程如果我们线上的机器是4核CPU理论上可以支持4个垃圾回收器并行执行可以提升4倍性能而ParNew采用多线程垃圾回收机制Serial采用单线程垃圾回收线上系统如何指定ParNew垃圾回收器IDEA设置Java -jar 跟上JVM参数Tomcat的catalina.sh中设置可以使用-XX:UseParNewGC选项我们在线上运行的使用使用了ParNew其会默认配置垃圾回收线程数和CPU核心数一样如果实在需要调整可以使用 -XX:ParanllelGCThreads参数设置JVM老年代垃圾回收器CMSCMS垃圾回收的原理我们老年代一般选择垃圾回收器是CMS也就是标记清除算法也就是将垃圾对象先标记处理然后一次性回收掉前面我们也分析过这样的算法的坏处是会造成大量内存碎片如果现在发送了Stop The World再去采用标记-清理的算法这个时候就会导致系统卡死时间过长很多响应无法处理使用CMS采用的是垃圾回收线程和系统工作线程尽量同时执行的模式来处理CMS如何实现一边工作的同时进行垃圾回收CMS执行一次垃圾回收分为4个阶段初始标记并发标记重新标记并发清理CMS垃圾回收性能分析最耗时的是老年代全部对相关进行GC Roots追踪标记处理哪些可以回收然后就是各种对象从内存里清理掉这是最耗时的线上部署系统如何设置垃圾回收相关参数并发回收垃圾导致CPU资源紧张虽然垃圾回收的同时系统可以工作但是在并发标记和并发清理两个地方十分耗时垃圾回收线程和系统工作线程同时工作会导致有限的CPU资源被垃圾回收线程占用并发标记的时候需要对GC Roots进行深度追踪但是老年代对象多这个过程十分耗时CMS默认启动的垃圾回收线程的数量是CPU3/4如果是2核CPU此时CMS会有个23/41个垃圾回收线程去占用1个CPU资源所以CMS并发垃圾回收机制第一个问题就是会消耗CPU资源Concurrent Mode Failure问题在并发清理阶段CMS只不过是回收之前标记好的垃圾回收对象但是这个系统一直在运行可能会随着系统运行让一些对象进入老年代同时还会变成垃圾对象这种垃圾对象是浮动垃圾这种对象要在下一次GC的时候才会清楚掉所以为了保证CMS垃圾回收期间还有一定空间可以进入老年代会预留一部分空间出来可以通过-XX:CMSInitiatingOccupancyFaction参数来设置老年代占用多少比例触发CMS垃圾回收JDK6默认是92%如果系统程序要放入老年代的对象大于了可用内存空间此时如何这个时候会发生Concurrent Mode Failure就是说并发垃圾回收失败了此时就会自动用Serial Old垃圾回收器代替CMS就是直接把系统Stop the World重新进行长时间的GC Roots追踪标记处理全部垃圾对象不允许产生新的对象内存碎片问题如果内存碎片太多了就会发生Full GC为了解决这个问题CMS不仅仅只是用标记-清理算法因为太多的内存碎片实际上会导致更加的频繁的Full GCCMS有一个参数-XX:UseCMSCompactAtFullCollection会默认打开其作用是Full GC之后会再次进行 Stop the World停止工作线程然后进行碎片整理就是把存活对象挪到一起空出来大片连续空间避免内存碎片每日上亿请求量的电商系统年轻的垃圾回收参数如何优化背景引入每日上亿请求量电商系统一般来说每个用户平均访问20来次计算上亿请求大概有500万日活用户按照10%付费转换率计算大概有50万人下单就算集中在每天4小时高峰期平均下来每秒钟10几个订单这样的情况没有什么需要关注的但是大促销的时候就不同短短10分钟就可能瞬间50万订单每秒有1000的下单请求抗住大促的瞬间压力需要多少机器基本上可以3台也就是每台机器抗下300下单请求部署配置为4核8G机器从机器本身角度来看是没有问题的但是我们需要对JVM有限的内存资源进行合理的分配和优化包括垃圾回收进行合理的优化让JVM的GC次数尽可能最少避免Full GC减少此影响大促下内存模型估算每秒钟300订单每个订单1kb大小300订单300kb大小内存开销带上其他业务对象需要对单个对象开销扩大10~20倍。还有许多其他订单相关操作如查询也就差不多在扩大10倍量。此时就有60Mb内存开销内存分配4核8G系统JVM内存一般会给4G堆内存空间给到3G新时代给1.5G老年代也是1.5G每个线程Java虚拟机栈有1MJVM有几百个线程大概有几百M然后永久代256MB内存基本上4G内存差不多同时还需要设置一些必要参数比如-XX:HandlePromotionFailure选项在JDK6以后老年代空间新时代对象总和或者老年代可用空间历次Minor GC升入老年代对象的平均大小两个条件满足一个直接进行Minor GC不需要提取出现Full GC新时代1.5G的内存空间大概需要25s会占满此时就会发生Minor GC大概可以回收99%新时代对象此时可能存活对象就100MB左右-XX:SurvivorRatio参数默认值为8此时Eden区位1.2GB内存每个Survivor是150MB所以Eden区大概就是1.2GB左右就要进行MinorGC只需要20s就把Eden区塞满了发生MinorGC然后GC存活对象在100MB左右会放入S1区域Survivor 空间够不够问题新时代垃圾回收在100MB左右可能破150MB这样就会频繁进入老年代还有即使Minor对象少于150MB但即使100MB对象进入Survivor区因为同龄对象直接超过了Survivor50%此时会导致对象进入老年代所以我们建议新时代调整为2G老年代调整为1G此时Eden区1.6G每个Survivor为200MB此时大大降低了进入老年代对象每日上亿请求量的电商系统老年的垃圾回收参数如何优化什么时候对象进入老年代第一中情况就是-XX:MaxTenuring Threshold5这个参数会让在一两分钟内连续躲过5次Minor GC的对象快速进入老年代一般是ServerController之类的注解标注的业务逻辑组件假设大概这个订单系统在大促期间每隔5分钟会在Minor GC之后有一小批对象进入老年代大概200MB左右没有打开-XX:HandlePromotionFailure选项结构老年代可用内存最多1G新生代对象总大小有1.8G会导致每次Minor GC前一检查都发现老年代可用内存新生代总对象大小会导致每次MinorGC都提取触发Full GC但是JDK6以后就抛弃了这个参数只需要满足下面条件直接触发Minor GC每次Minor GC之前都会检查老年代可用内存空间历次Minor GC后升入老年代的平均大小可能某次Minor GC后升入老年代的对象几百MB但是老年代可用空间不足设置了-XX:CMSInitiatingOccupancyFaction参数比如设定了92%也就是老年代内存超过了这个就会触发Full GC
返回列表