1. 概述
之前的文章中介绍了 jvm 内存管理和垃圾收集的相关内容,结合这些理论知识,通过合理设置参数才能将系统的性能得以提升。
2. JVM 主要参数
2.1. 基本的设置参数
JVM 设置参数的主要含义
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-Xms | 最小堆大小 | 物理内存的 1/64(<1GB) | 空闲堆内存小于40%(通过 MinHeapFreeRatio 参数可调整该阈值)时,jvm 会增大堆直到 -Xmx |
-Xmx | 最大堆大小 | 物理内存的 1/4(<1GB) | 空闲堆内存大于70%(通过 MaxHeapFreeRatio 参数可以调整该阈值)时,jvm 会减小堆直到 -Xms |
-Xmn | 新生代大小 | 无 | 指 Eden 与两个 Survivor 空间之和,Sun 官方推荐配置为整个堆的 3/8 |
-XX:PermSize | 方法区(永久代)大小 | 物理内存的 1/64 | - |
-Xss | 每个线程的堆栈大小 | 1M | - |
-XX:ThreadStackSize | 线程堆栈大小 | 1M | 主线程以 -Xss 设置为主,其他线程以该设置为主,一般采用默认值即可 |
-XX:NewRadio | 新生代与老年代大小的比值 | 无 | Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置 |
-XX:SurvivorRatio | Eden 区域 Survivor 区大小的比值 | 无 | - |
-XX:LargePageSizeInBytes | 内存页大小 | 128M | 不可设置过大 |
-XX: UseFastAccessorMethods | 是否使用原始类型的快速优化 | 无 | - |
-XX: DisableExplicitGC | 是否关闭 System.gc() | 无 | - |
-XX:MaxTenuringThreshold | 垃圾最大年龄 | 无 | N 次没有被回收的新生代资源自动放入老年代,只有使用串行GC时才有效 |
-XX:MaxGCPauseMillis | 最大停顿时间 | - | 每次年轻代垃圾收集的最长时间 |
-Xnoclassgc | 金庸垃圾回收 | 无 | - |
-XX:SoftRefLRUPolicyMSPerMB | 没M堆内存中软引用存活时间 | 1s | - |
-XX:PretenureSizeThreshold | 对象超过多大自动在老年代分配 | 0 | 采用 Parallel Scavenge GC 时无效 |
-XX:TLABWasteTargetPercent | TLAB 占 eden 区的百分比 | 1% | - |
-XX: CollectGen0First | FullGC时是否先YGC | false | - |
2.2. 并行收集相关的参数
JVM 并行GC的设置参数
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-XX: UseParGC | 使用 ParNew 收集器进行新生代收集 | false | 与 -XX: UseConcMarkSweepGC 相同 |
-XX:ParallelGCThreads | 并行收集线程数 | - | 指定并行 GC 下的垃圾收集线程数,最好配置为与 CPU 数相等 |
-XX: UseParallelOldGC | 设置老年代收集器为 Parallel Old | JDK1.6 起开始提供 | - |
-XX: UseAdaptiveSizePolicy | 自动选择年轻代大小及 Survivor 比例 | false | 建议在使用并行收集器一直打开 |
-XX:GCTimeRatio | 设置垃圾回收时间占用程序运行时间百分比 | 无 | 大于 0 小于 100,使用 Parallel Scavenge 时设置 |
-XX: UseAdaptiveSizePolicy | 是否自动根据当前系统情况决定最大吞吐量的限制 | false | 使用 Parallel Scavenge 时设置 |
-XX: ScavengeBeforeFullGC | Full GC前调用YGC | true | - |
2.3. CMS 相关参数
JVM CMS GC 的设置参数
参数名称 | 含义 | 默认值 | 说明 |
---|---|---|---|
-XX: UseConcMarkSweepGC | 启用 CMS GC | false | - |
-XX:CMSFullGCsBeforeCompaction | 运行多少次 GC 后进行一次内存压缩(碎片整理) | - | - |
-XX: CMSParallelRemarkEnabled | 降低标记停顿 | false | - |
-XX UseCMSCompactAtFullCollection | 是否启用内存压缩(碎片整理) | - | 建议开启 |
-XX: UseCMSInitiatingOccupancyOnly | 手动触发 CMS | false | 禁止 HotSpot 自动触发 CMS GC |
-XX:CMSInitiatingOccupancyFraction | GC 触发阈值(百分比) | 1.5为68,1.6为92 | 内存使用达到阈值则开始 GC,设置过大会有可能造成失败而进行 full GC |
-XX: CMSIncrementalMode | 设置为增量模式 | false | 运用于单CPU下,在 CMS 运行途中暂停,继续运行用户线程,下次增量 GC |
-XX:CMSClassUnloadingEnabled | 垃圾回收清理方法区时移除不再使用的 classes | - | - |
-XX: CMSPermGenSweepingEnabled | 是否清理方法区(永久代) | - | 默认不清理 |
3. 设置规则
3.1. 年轻代大小选择
1. 响应时间优先的应用 — 尽可能增大年轻代大小,这也意味着老年代的大小会相对减少,因此同时要减少到达老年代的对象 2. 吞吐量优先应用 — 尽可能增大年轻代,可以选择并行垃圾收集,适合 8CPU 以上应用
无论如何,谨记不能将新生代设置过小,否则会造成新生代 GC 频繁,甚至让新生代对象直接进入老年代,从而触发 full GC。
3.2. 老年代大小选择
1. 响应时间优先的应用 — 通常老年代使用 CMS 进行并发收集,所以老年代不能设计过小,否则会因为内存过多造成频繁 full GC;如果设计过大,则需要较长的收集,因此需要结合并发收集信息、持久化并发收集次数、传统GC信息、年轻代和老年代的时间比例考虑具体的内存大小 2. 吞吐量优先应用 — 这样的应用通常需要大年轻代 小老年代,这样可以尽可能回收大部分短期对象,减少中期对象,而老年代存放长期存活对象
3.3. 老年代的内存碎片问题
如果使用 CMS 作为老年代收集器,那么由于他采取的标记清除算法,通常会造成碎片,如果最终空间不足,则会触发一次 full GC。 针对这种情况,需要进行如下配置: 1. -XX: UseCMSCompactAtFullCollection — 使用并发收集器时,开启对年老代的压缩 2. -XX:CMSFullGCsBeforeCompaction=0 — 上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
3.4. 其他事项
1. linux 64 位操作系统中,jdk 消耗内存更多,执行速度更慢,但吞吐量更大 2. XMX 和 XMS 设置一样大,MaxPermSize 和 MinPermSize 设置一样大,这样可以减轻伸缩堆大小带来的压力 3. 使用CMS的好处是用尽量少的新生代,经验值是128M-256M, 然后老生代利用CMS并行收集, 这样能保证系统低延迟的吞吐效率。 实际上cms的收集停顿时间非常的短,2G的内存, 大约20-80ms的应用程序停顿时间 4. 系统停顿的时候可能是GC的问题也可能是程序的问题,多用jmap和jstack查看,或者killall -3 java,然后查看java控制台日志,能看出很多问题。 5. 如果用了缓存,那么年老代应该大一些 6. 采用并发回收时,年轻代小一点,年老代要大,因为年老大用的是并发回收,即使时间长点也不会影响其他程序继续运行,网站不会停顿
4. 问题分析 — promotion failed
产生 promotion failed 可能是两个原因引起的: 1. 新生代的 survivor 不足,年轻代中很多对象等待放入 survior 内存,致使 Survivor 空间不足,这些内存被放入老年代 2. 老年代没有足够的内存接纳来自年轻代的对象
解决这个问题,就要从这两个原因着手: 1. 增大 Survivor 区域大小,设置 -XX:SurvivorRatio=1 2. 降低 CMSInitiatingOccupancyFraction(如设置为 70),让老年代在较低阈值时被回收
可以推断出:eden from survivor < old gen区剩余内存时,不会出现promontion faild的情况,即: (Xmx-Xmn)(1-CMSInitiatingOccupancyFraction/100)>=(Xmn-Xmn/(SurvivorRatior 2)) 得到: CMSInitiatingOccupancyFraction <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior 2)))/(Xmx-Xmn)100。