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

线上服务运行缓慢,老大让我调JVM参数

时间:2023-03-18 15:42:16 科技观察

大家好,我是北军。最近线上服务运行缓慢,老大让我优化一下JVM。GC内容很多,不可能一下子全部掌握。今天我们就来看看G1的一些知识,以及调校时可以调整的参数。一、G1简介G1的全称是GarbageFirstGarbageCollector,是HotSpotJVM内置的服务端垃圾收集器。G1作为CMS的替代品出现,其目标是在满足最短暂停时间的同时实现高吞吐量,适用于多核处理器和大内存容量的系统。其特点是:停顿时间短、可控。内存分区可以应用在大内存系统中,使用基于内存的新生代收集和混合收集。高吞吐量。设计了一个新的并发标记线程来并发处理分区之间的引用关系并加速垃圾收集。1.1分区G1垃圾收集器将堆内存划分为固定大小的区域。下图是G1内存分配示意图,其中灰色格子代表一个区域。其中,G1的分区可以分为四种:FreeHeapRegion(FHR)、YoungHeapRegion(YHR)、YoungHeapRegion(YHR)。Region可以分为大对象头分区和大对象连续分区。大对象一般占Region的一半以上。OldHeapRegion(OHR)Region可以是1MB~32MB,是2的N次方。设置分区大小有以下方法:可以通过-XX:G1HeapReginotallow=<>指定大小,默认为0。默认情况下,整个堆被划分为2048个Region。1.1.1新生代的大小新生代的大小设置如下:如果设置了最大值(MaxNewSize)和最小值(NewSize),则Xmn等价于MaxNewSize。如果设置了最大值和最小值,并且设置了NewRatio,则忽略NewRatio。新生代的最大值和最小值没有设置,但是如果设置了NewRatio,那么新生代的最大值和最小值是一样的,都是整个堆空间(NewRatio+1)。如果新生代的最大值和最小值没有设置,或者只设置了最大值或最小值之一,G1会根据参数G1MaxNewSizePrecent(默认值60)和G1NewSizePercent(默认值5)到整个堆空间。关于堆大小的参数优化:G1HeapRegionSize可以指定堆的大小,并且可以指定或具有内存管理启发式来推断分区大小。xms/xmx指定堆空间的最小值/最大值。一定要设置正确的值,否则会影响分区大小的推断。G1不设置MaxNewSize、NewSize、Xmn、NewRatio,即不显式设置新生代的大小。G1对内存的管理不是连续的,所以即使重新分配一个堆分区的代价也不高;G1的目标是满足垃圾回收暂停,这就需要G1根据暂停时间动态调整回收的分区。如果设置固定的分区数,G1新生代的大小无法调整的话,就不容易满足停顿时间的需要。GCTimeRatio是指GC与应用程序的时间比例。默认值为9,表示GC和程序的时间比例为90%。增加这个值会减少GC占用的时间,增加这个值会更容易动态扩展内存。2.G1GC可以优化参数G1提供两种GC模式,YoungGC和MixedGC。两个GC都会有STW。YoungGC主要是对Eden区进行GC。一般当EdenRegion的使用达到最大阈值,空间内存不够时,就会触发YoungGC。每次YoungGC回收所有Eden和Serviour区,并将存活的对象复制到Old区和部分Survivor区。MixedGCMixedGC会选择(并发标记)所有YoungRegion和部分回收收益较高的OldRegion,然后进行YoungGeneration回收算法。混合恢复分为两个阶段。并发标记垃圾回收并发标记阶段可以分为以下几个子阶段:初始标记子阶段:标记所有直接可达的根对象,这个阶段会是STW,并发标记子阶段:YoungGC执行完成后,如果并发标记满足条件(已分配和待分配内存占总内存的比例超过阈值后),进行并发标记,其中-XX:ConcGCThreads控制并发标记线程数,一个线程扫描一个一次区域。这时候标记存活对象,然后标记子阶段:找出所有未被访问的存活对象。这个过程是并发执行的,会有STW,其中-XX:ParallelGCThreads可以指定GC暂停时可用的GC线程数。清理子阶段:需要STW,存活对象计数,排序标记位图,释放完全空闲分区。混合回收阶段参数优化:参数InitiatingHeapOccupancyPercent(IHOP),默认值为45,这个值是启动并发标记的前提,只有分配的内存占总空间的45%以上后,并发标记任务将开始。增加这个值会导致并发标记花费更多的时间,同时也会减少YGC或MixedGC时收集的分区数量,从而导致更多的FullGC。这个值可以根据整个应用程序占用的平均内存来设置,这个值可以设置的比平均内存稍微高一点。IHOP的设置效果很明显,但是要设置一个合理的值并不容易,需要更多的性能测试来判断。参数G1ReservePercent,默认值为10,如果GC提升失败导致FullGC,可以增加该值参数ConcGCThreads为并发线程数,默认值为0,如果不设置,可以动态调整,以及以ParallelGCThreads为依据推断,如果Concurrent标记耗时较长,可以增加并发线程数。HeapSizePerGCThread默认是64M,也就是说每64M分配一个线程参数UseDynamicNumberOfGCThreads。默认为false,设置为true表示可以动态调整线程数。调整范围会根据最大线程数HeapSizePerGCThread来决定。参数GCDrainStackTargetSize,默认值为64,表示并发标记子阶段,一次标记的最大对象数。参数GCMixedGCLiveThresholdPercent的默认值为85,用于判断分区是否可以加入到CSet中,低于该值则加入。参数G1HeapWastePercent的默认值为5,即当Cset中可回收空间占总空间的比例大于G1HeapWastePercent时,将开始混合回收。参数G1MixedGCCountTarget,默认值为8。该参数越大,老年代回收的分区越少。否则,将收集更多的分区。尽量让Cset中老年代分区的比例保持在1/G1MixedGCCountTarget以上。G1OldCSetRegionThresholdPercent参数默认值为10,表示最多可以收集10%的分区。G1ConcMarkStepDurationMillis参数默认值为10,即每个并发标记子阶段最多执行10ms。FullGC发生后,基本就是串行回收。如果不幸发生FullGC,那么我们能做的就是尽量让FullGC尽快完成,然后降低它的频率。但通常情况下,相对固定且时间间隔较长的FullGC是允许的。然后是FullGC相关的一些优化调整:使用参数MinHeapFreeRatio来判断堆空间是否可以扩展,增大该值扩展的概率会降低。MaxHeapFreeRatio决定了空间是否可以收缩,这个值越大收缩的概率越小。MarkSweepAlwaysCompactCount默认值为4,这个值的意思是在经过一定次数的GC后,允许当前区域一定比例的死对象作为存活对象,暂时不回收,从而加快FullGC的处理过程.这个比例可以使用MarkSweepDeadRatio进行修改,默认值为5。综上所述,以上就是一些优化参数的使用。至于具体调优的目的,就要看我们各种程序的要求了。一般来说,要满足最大吞吐量和最小停顿时间,GC频率越低越好,堆空间的有效利用率要高。可调整的部分包括内存参数优化、引用处理(Rset)、并发标记(Mark)、垃圾回收等。Oracle官方有一些推荐的调优方向:对于新生代的设置,尽量避免显式设置新生代的大小(使用-Xmn、-XX:NewRatio等),固定的新生代大小会覆盖最小停顿时间的目标。对于暂停时间的目标,我们需要考虑延迟和吞吐量之间的平衡。两者不能兼得,所以我们需要找到一个最佳的平衡点。混合回收阶段的优化参数可以考虑使用-XX:InitiatingHeapOccupancyPercent修改内存使用比例(具体可以参考上一篇文章),-XX:G1MixedGCLiveThresholdPercent和-XX:G1HeapWastePercent改变混合策略垃圾回收,-XX:G1MixedGCCountTarget和-XX:G1OldCSetRegionThresholdPercent调整CSet中老年代的比例参考:https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning。网页格式