<>一、JVM常见参数
<>1、标准参数
-verbose:class 打印每个class信息 -verbose:gc 打印每次gc信息
<>2、非标参数 -X
-Xlog:gc:filename 设置GC log文件的位置 -Xlog:gc:./gc-myapp.log -Xms大小 设置堆的初始化大小
-Xmx2048m=-XX:InitialHeapSize -Xmx大小 设置堆的最大大小 -Xms1024m =-XX:MaxHeapSize 一般Xms=
Xmx,防止扩容和缩容 -Xmn大小 设置年轻代大小(初始化和最大) -Xmn256m -XX:NewSize -XX:MaxNewSize
分别指定年轻代的初始化和最大大小,建议年轻代占堆大小的1/4 ~1/2 -Xss大小 设置线程栈大小 -Xss1m =-XX:ThreadStackSize
<>3、不稳定参数 -XX
-XX:ErrorFile=文件 设置错误日志路径 -XX:ErrorFile=./hs_err_pid%p.log %p为当前进程号 -XX:OnError
=命令 错误发生时执行命令 -XX:OnError="gcore %p;dbx - %p" -XX:OnOutOfMemoryError=命令
内存溢出时执行命令 -XX:MaxDirectMemorySize=size 设置直接内存最大值 -XX:MaxDirectMemorySize=100m
默认为0 当直接内存达到设置的最大值会FullGC -XX:ObjectAlignmentInBytes=alignment
设置java对象的内存对齐,默认是8字节 -XX:ThreadStackSize 设置线程栈大小 -XX:ThreadStackSize=1m = -Xss
-XX:-UseBiasedLocking 禁用偏向锁 默认开启,不禁用 如果使用的是大量的没有竞争的同步,使用偏向锁会提升性能
-XX:-UseCompressedOops 禁用压缩指针堆内存小于32G时默认开启
开启后,对象引用是32位而不是64位,可以提升性能。只有64位的jvm才生效 -XX:+DoEscapeAnalysis 开启逃逸分析 默认开启
-XX:+Inline 开启方法内联 默认开启 -XX:InlineSmallCode=大小 设置应内联的已编译方法的最大代码大小,只有小于此数值的才会内联
默认1000字节 -XX:MaxInlineSize=大小 设置要内联方法的最大字节码大小 默认35字节 -XX:+OptimizeStringConcat
开启字符串连接优化 默认开启 -XX:+PrintInlining 打印方法内联 默认关闭,需和-XX:+UnlockDiagnosticVMOptions
一起使用 -XX:-TieredCompilation 关闭分层编译 默认开启 -XX:+HeapDumpOnOutOfMemoryError
OOM时堆内存dump到当前目录 -XX:HeapDumpPath=路径 设置堆内存dump的路径 -XX:HeapDumpPath=
/var/java_pid%p.hprof -XX:+UnlockDiagnosticVMOptions 开启jvm诊断功能选项 垃圾回收相关参数
-XX:+AggressiveHeap 开启堆最优化设置 默认关闭 -XX:+CMSClassUnloadingEnabled
当使用CMS垃圾收集器时,允许类卸载 默认开启 -XX:CMSExpAvgFactor=percent 指定垃圾收集消耗的时间百分比
默认这个数是25%,就是25 -XX:CMSInitiatingOccupancyFraction=percent 设置CMS回收开始的老年代百分比
默认-1,任何的负值表示会使用-XX:CMSTriggerRatio选项来定义这个百分比数 -XX:+CMSScavengeBeforeRemark
在CMS重新标记之前执行ygc操作 默认关闭 在remark时间过长时可以开启;开启减少remark的STW时间 -XX:CMSTriggerRatio=
percent 设置CMS开始的百分比 默认80,((100 - MinHeapFreeRatio) + (double)( CMSTriggerRatio
* MinHeapFreeRatio) / 100.0) / 100.0=92% -XX:+UseCMSCompactAtFullCollection
在FULL GC的时候,对年老代的压缩 -XX:CMSFullGCsBeforeCompaction=0 上面配置开启的情况下,这里设置多少次Full
GC后,对年老代进行压缩, -XX:MinHeapFreeRatio=percent GC之后堆内存最小剩余百分比,如果小于此值,则自动扩容 默认40%
-XX:MaxHeapFreeRatio=percent GC之后堆内存最大剩余百分比,如果小于此值,则自动缩容 默认70%
-XX:ParallelGCThreads=threads 设置Parallel GC的线程数 默认根据cpu个数,<=8,则使用8个,>8
个3+5N/81台服务器只有1个jvm时使用默认值较好,如果有n个jvm,cpu个数 / n比较合适 -XX:ConcGCThreads=个数
并发GC的线程数 默认值取决于cpu个数 ConcGCThreads= (ParallelGCThreads + 3)/4
-XX:+DisableExplicitGC 使System.gc()显式gc失效 默认不开启, -XX:G1HeapRegionSize=size
当使用G1收集器时,设置java堆被分割的region大小 1M~32M默认根据堆内存最优化设置 -XX:+G1PrintHeapRegions
打印G1收集器收集的区域 默认关闭 -XX:G1ReservePercent=percent 设置堆内存保留大小,以防晋升失败 0~50 默认10%
-XX:InitialHeapSize=size 堆初始大小 =-Xms -XX:MaxHeapSize=size 堆最大大小 =-Xmx
-XX:InitialSurvivorRatio=ratio 设置伊甸园区和幸存区初始比例 默认为8:1 -XX:SurvivorRatio=ratio
设置伊甸园区和幸存区比例 默认为8:18:1:1 XX:TargetSurvivorRatio YGC之后,幸存区期望百分比 默认 50%
XX:InitiatingHeapOccupancyPercent=percent 堆占用达到多少开始并发垃圾回收 只有并发垃圾回收器生效
-XX:MaxGCPauseMillis=time GC最大暂停时间 ms 默认没有最大暂停时间 -XX:MetaspaceSize=size
元空间多次扩容后超过此值就会full gc 默认大约20M 元空间使用本地内存。 尽量设置足够,避免因为这个区域内存不够引发Full GC
-XX:MaxMetaspaceSize=size 设置元空间最大大小 默认不限制 建议和MetaspaceSize一样大,一般256M -XX:NewSize
=size 设置年轻代初始大小 建议年轻代占堆大小的1/4 ~ 1/2 -XX:MaxNewSize=size 设置年轻代最大大小 默认根据最大效能分配
-XX:MaxTenuringThreshold=threshold 最大晋升年龄,从年轻代到老年代 默认:15 - 并行回收器 6 - CMS
-XX:NewRatio=ratio 设置老年代和新生代比例 默认2 老年代 : (伊甸园 + 2个幸存区) -XX:+PrintGC 打印GC信息 默认关闭
-XX:+PrintGCDetails 打印GC详细信息 默认关闭 -XX:+PrintTenuringDistribution 打印晋升分配
-XX:+ScavengeBeforeFullGC Full gc之前先ygc 默认开启 -XX:+UseTLAB 年轻代使用线程局部缓存 默认开启 效率高
-XX:TLABSize=size 设置初始化thread-local allocation buffer (TLAB)大小
-XX:+UseAdaptiveSizePolicy JDK1.8 默认使用 UseParallelGC 垃圾回收器,该垃圾回收器默认启动了
AdaptiveSizePolicy -XX:+UseParallelGC 年轻代使用并行回收器 -XX:+UseParallelOldGC
老年代使用并行回收器 -XX:+UseParNewGC 为配置CMS,年轻代使用ParNew回收器,CMS会默认开启
-XX:+UseConcMarkSweepGC 使用CMS回收器 -XX:+UseG1GC 使用G1回收器 -XX:+UseGCOverheadLimit
限制GC的运行时间,通过统计GC时间来预测是否要OOM了,提前抛出异常,防止OOM发生
并行/并发回收器在GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。用来避免内存过小造成应用不能正常工作
-XX:+UseStringDeduplication 开启字符串去重 G1回收器生效 -XX:AutoBoxCacheMax=20000 加大Integer
Cache -XX:+PrintPromotionFailure 知道是多大的新生代对象晋升到老生代失败从而引发Full GC时的。
<>二、G1GC
<>G1GC
G1 GC是启发式算法,会动态调整年轻代的空间大小。目标也就是为了达到接近预期的暂停时间。
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。
<>Young GC
Young
GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。
<>Mixed GC
Mix GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。
它的GC步骤分2步: 全局并发标记(global concurrent marking) 拷贝存活对象(evacuation)
在进行Mix GC之前,会先进行global concurrent marking(全局并发标记)。 global concurrent
marking的执行过程是怎样的呢?
在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。
global concurrent marking的执行过程分为五个步骤:
* 初始标记(initial mark,STW)在此阶段,G1 GC 对根对象进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。
* 根区域扫描(root region scan)G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非
STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。
* 并发标记(Concurrent Marking)G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW
年轻代垃圾回收中断
* 最终标记(Remark,STW)该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。
* 清除垃圾(Cleanup,STW)在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC
会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。
Young GC:选定所有新生代里的region。通过控制新生代的region个数来控制young GC的开销。
Mixed GC:选定所有新生代里的region,外加根据global concurrent
marking统计得出收集收益高的若干老年代region。在用户指定的开销目标范围内尽可能选择收益高的老年代region。
在G1中,有一种特殊的区域,叫Humongous区域。
如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在年老代。
<>三、G1调优手册
暂停时间:用-XX:MaxGCPauseMillis来指定,默认值200ms。这是一个软性目标,G1会尽量达成,如果达不成,会逐渐做自我调整。对于Young
GC来说,会逐渐减少Eden区个数,减少Eden空间那么Young GC的处理时间就会相应减少;对于Mixed GC,G1会调整每次Choose
Cset的比例,默认最大值是10%,当然每次选择的Cset少了,所要经历的Mixed
GC的次数会相应增加。同时减少Eden的总空间时,就会更加频繁的触发Young GC,也就是会加快Mixed GC的执行频率,因为Mixed
GC是由Young GC触发的,或者说借机同时执行的。频繁GC会对对应用的吞吐量造成影响,每次Mixed
GC回收时间太短,回收的垃圾量太少,可能最后GC的垃圾清理速度赶不上应用产生的速度,那么可能会造成串行的Full
GC,这是要极力避免的。所以暂停时间肯定不是设置的越小越好,当然也不能设置的偏大,转而指望G1自己会尽快的处理,这样可能会导致一次全部并发标记后触发的Mixed
GC次数变少,但每次的时间变长,STW时间变长,对应用的影响更加明显。
Region大小
:用-XX:G1HeapRegionSize来指定,若未指定则默认最多生成2048块,每块的大小需要为2的幂次方,如1,2,4,8,16,32,最大值为32M。Region的大小主要是关系到Humongous
Object的判定,当一个对象超过Region大小的一半时,则为巨型对象,那么其会至少独占一个Region,如果一个放不下,会占用连续的多个Region。当一个Humongous
Region放入了一个巨型对象,可能还有不少剩余空间,但是不能用于存放其他对象,这些空间就浪费了。所以如果应用里有很多大小差不多的巨型对象,可以适当调整Region的大小,尽量让他们以普通对象的形式分配,合理利用Region空间。
新生代比例:一般不需要设置新生代大小。让G1自己根据最大停顿时间动态调整。新生代比例有两个数值指定,下限:
-XX:G1NewSizePercent,默认值5%,上限:-XX:G1MaxNewSizePercent,默认值60%。G1会根据实际的GC情况(主要是暂停时间)来动态的调整新生代的大小,主要是Eden
Region的个数。最好是Eden的空间大一点,毕竟Young GC的频率更大,大的Eden空间能够降低Young GC的发生次数。但是Mixed
GC是伴随着Young GC一起的,如果暂停时间短,那么需要更加频繁的Young GC,同时也需要平衡好Mixed
GC中新生代和老年代的Region,因为新生代的所有Region都会被回收,如果Eden很大,那么留给老年代回收空间就不多了,最后可能会导致Full GC。
并发GC线程数:通过
-XX:ConcGCThreads来指定,默认是-XX:ParallelGCThreads/4,也就是在非STW期间的GC工作线程数,当然其他的线程很多工作在应用上。当并发周期时间过长时,可以尝试调大GC工作线程数,但是这也意味着此期间应用所占的线程数减少,会对吞吐量有一定影响。
并行GC线程数:通过 -XX:ParallelGCThreads来指定,也就是在STW阶段工作的GC线程数,其值遵循以下原则:
① 如果用户显示指定了ParallelGCThreads,则使用用户指定的值。
② 否则,需要根据实际的CPU所能够支持的线程数来计算ParallelGCThreads的值,计算方法见步骤③和步骤④。
③
如果物理CPU所能够支持线程数小于8,则ParallelGCThreads的值为CPU所支持的线程数。这里的阀值为8,是因为JVM中调用nof_parallel_worker_threads接口所传入的switch_pt的值均为8。
④
如果物理CPU所能够支持线程数大于8,则ParallelGCThreads的值为8加上一个调整值,调整值的计算方式为:物理CPU所支持的线程数减去8所得值的5/8或者5/16,JVM会根据实际的情况来选择具体是乘以5/8还是5/16。
比如,在64线程的x86 CPU上,如果用户未指定ParallelGCThreads的值,则默认的计算方式为:ParallelGCThreads = 8
+ (64 - 8) * (5/8) = 8 + 35 = 43。
被纳入Cset的Region的存活空间占比阈值:通过
-XX:G1MixedGCLiveThresholdPercent指定,不同版本默认值不同,有65%和85%。在全局并发标记阶段,如果一个Region的存活对象的空间占比低于此值,则会被纳入Cset。此值直接影响到Mixed
GC选择回收的区域,当发现GC时间较长时,可以尝试调低此阈值,尽量优先选择回收垃圾占比高的Region,但此举也可能导致垃圾回收的不够彻底,最终触发Full
GC。
触发全局并发标记的老年代使用占比
:通过-XX:InitiatingHeapOccupancyPercent指定,默认值45%,也就是老年代占堆的比例超过45%。如果Mixed
GC周期结束后老年代使用率还是超过45%,那么会再次触发全局并发标记过程,这样就会导致频繁的老年代GC,影响应用吞吐量。同时老年代空间不大,Mixed
GC回收的空间肯定是偏少的。可以适当调高IHOP的值,当然如果此值太高,很容易导致年轻代晋升失败而出发Full GC,所以需要多次调整测试。
触发Mixed GC的堆垃圾占比
:通过-XX:G1HeapWastePercent指定,默认值5%,也就是在全局标记结束后能够统计出所有Cset内可被回收的垃圾占整对的比例值,如果超过5%,那么就会触发之后的多轮Mixed
GC,如果不超过,那么会在之后的某次Young GC中重新执行全局并发标记。可以尝试适当的调高此阈值,能够适当的降低Mixed GC的频率。
每轮Mixed GC回收的Region最大比例
:通过-XX:G1OldCSetRegionThresholdPercent指定,默认10%,也就是每轮Mixed
GC附加的Cset的Region不超过全部Region的10%,最多10%,如果暂停时间短,那么可能会少于10%。一般这个值不需要额外调整。
一个周期内触发Mixed GC最大次数
:通过-XX:G1MixedGCCountTarget指定,默认值8。也就是在一次全局并发标记后,最多接着8此Mixed
GC,也就是会把全局并发标记阶段生成的Cset里的Region拆分为最多8部分,然后在每轮Mixed
GC里收集一部分。这个值要和上一个参数配合使用,8*10%=80%,应该来说会大于每次标记阶段的Cset集合了。一般此参数也不需额外调整。
G1为分配担保预留的空间比例
:通过-XX:G1ReservePercent指定,默认10%。也就是老年代会预留10%的空间来给新生代的对象晋升,如果经常发生新生代晋升失败而导致Full
GC,那么可以适当调高此阈值。但是调高此值同时也意味着降低了老年代的实际可用空间。
谨慎使用Soft Reference
:如果SoftReference过多,会有频繁的老年代收集。-XX:SoftRefLRUPolicyMSPerMB参数,可以指定每兆堆空闲空间的软引用的存活时间,默认值是1000,也就是1秒。可以调低这个参数来触发更早的回收软引用。如果调高的话会有更多的存活数据,可能在GC后堆占用空间比会增加。
对于软引用,还是建议尽量少用,会增加存活数据量,增加GC的处理时间。
晋升年龄阈值:通过-XX:MaxTenuringThreshold指定,默认值15。一般新生对象经过15次Young
GC会晋升到老年代,巨型对象会直接分配在老年代,同时在Young GC时,如果相同age的对象占Survivors空间的比例超过
-XX:TargetSurvivorRatio的值(默认50%),则会自动将此次晋升年龄阈值设置为此age的值,所有年龄超过此值的对象都会被晋升到老年代,此举可能会导致老年代需要不少空间应对此种晋升。一般这个值不需要额外调整。
<>四、CMS参数样例
假如堆内存分配4G,新生代3G,老年代1G,Eden区2.4G,Survivor区各300M,一般来说YoungGC后存活的对象小于150M就没太大问题
元空间给个 512M 一般就足够了,如果系统会运行时创建很多类,可以调大这个值 -XX:MaxTenuringThreshold
对象GC年龄调整为5岁,让长期存活的对象更快的进入老年代 -XX:PretenureSizeThreshold
大对象阀值设置为1M,如果有超过1M的大对象,可以调整下这个值
-XX:+UseParNewGC、-XX:+UseConcMarkSweepGC,垃圾回收器使用 ParNew + CMS 的组合
-XX:CMSFullGCsBeforeCompaction设置为0,每次FullGC后都进行一次内存碎片整理
-XX:+CMSParallelInitialMarkEnabled,CMS初始标记阶段开启多线程并发执行,降低FullGC的时间
-XX:+CMSScavengeBeforeRemark,CMS重新标记阶段之前,先尽量执行一次Young GC
-XX:+DisableExplicitGC,禁止显示手动GC,如果系统使用了堆外内存就不要禁用
-XX:+HeapDumpOnOutOfMemoryError,OOM时导出堆快照便于分析问题 -XX:+PrintGC,打印GC日志便于出问题时分析问题
<>五、G1参数样例
JDK11(不同版本有些参数可能失效了)
-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4M
-XX:MaxTenuringThreshold=5 -XX:InitiatingHeapOccupancyPercent=40
-XX:ConcGCThreads=4 //当前核心数的一半 -XX:+UseStringDeduplication -XX:AutoBoxCacheMax=
20000 -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m
-XX:+PrintPromotionFailure -XX:+HeapDumpOnOutOfMemoryErro -XX:HeapDumpPath=./
-Xlog:gc*:./gc-myapp.log -XX:+DisableExplicitGC //禁止显示手动GC,如果系统使用了堆外内存就不要禁用
<>六、经验之谈
* 关闭 SWAP 分区。
swap 是很多性能场景的万恶之源,建议禁用。进程倒是死不了,但 GC 时间长的却让人无法忍受。特别是ES服务更要关闭SWAP
* 老年代占用不高却经常发生FullGC 有可能是MetaSpace空间不足导致的
* 常见内存溢出原因:使用了 HashMap 做缓存,但是并没有设置超时时间或者 LRU 策略,造成了放入 Map 对象的数
据越来越多,而产生了内存泄漏
没有重写 Key 类的 hashCode 和 equals 方法,造成了放入 HashMap
的所有对象都无法被取出来,它们和外界失联了。尽量避免使用自定义的对象作为 Key。