当前位置: 首页 > 科技观察

说说JVM调优的两个小知识点

时间:2023-03-22 00:12:28 科技观察

JVM调优需要很多场景和经验。本文主要从理论的角度给大家介绍一下。我把堆区的主要结构和参数放在下面,大家可以参考图看看:1.如何设置最大年龄每次YoungGC发生时,Eden区和当前Survivor区的存活对象会一次性进入另一个Survivor区,清空之前的Eden区和Survivor区。因此,年轻代的存活对象基本上是在两个Survivor区之间交换,每交换一次age就增加1年。当达到最大年龄时(最大年龄由-XX:MaxTenuringThreshold参数设置,默认15岁),将转移到老年代。现在有这样一个场景,有1000个8岁的对象,过了一段时间,有900个15岁的对象。可以观察到8岁以后,90%的对象都达到了默认的最大年龄,这些对象不停地在两个Survivor区之间切换,这无疑增加了复制的成本。因此,在这种情况下,我们可以将最大年龄设置为8岁,将年龄达到8岁的对象直接转移到老年代,避免在新生代重复复制浪费空间。2、YoungGC频繁怎么办?我们使用jstat-gcutil{pid}1000,即每秒打印出GC统计信息,其中YGC代表YoungGC发生的总数。统计信息每秒刷新一次。如果此时发现YGC频繁增加,比如YoungGC每秒一次。YoungGC频繁,意味着新对象的创建速度与新生代的大小不匹配,要么是代码中频繁创建对象,要么是新生代的空间太小。故障排除代码是必要的,但很耗时。所以这次主要从调整新生代大小的方案入手。我们可以把新生代区域增加到1.5倍(为什么是1.5倍,这只是一个暂定的倍数)。如果之前YoungGC每1000ms发生一次,理论上YoungGC间隔1500ms左右,频率降低,但会不会导致YoungGC耗时增加1.5倍?答案是不。YoungGC主要清理新生代。首先标记Eden区和一个Survivor区的存活对象,然后一起复制到另一个Survivor区,最后直接清理Eden区和之前的Survivor区。可以看出,这里最耗时的环节是复制操作。大约98%的对象会在几毫秒内死亡。即使新生代扩大到原来的1.5倍,那么当下一次YoungGC到来时,复制的对象总数也会比之前的1.5倍小很多,可能只会比之前多一点点,比如1.15倍。因此,将新生代扩大到原来的1.5倍,理论上扫描新生代的时间会变成原来的1.5倍,标记时间在[1,1.5)倍以内,复制时间在[1,1.5)倍以内,1.5)倍,而这两次比1.5倍小很多。对于虚拟机,复制比扫描和标记操作要昂贵得多。因此,在扩容新生代后,YoungGC不会有明显的线性增长。如果整个堆的大小保持不变,新生代扩大后老年代的空间必然会被压缩,MajorGC的频率可能会增加。所以还是需要找到一个临界点,什么时候可以大幅度降低YoungGC的频率,而MajorGC的频率只能小幅度的提高。