一、JVM-GC情况
- YGC统计: 30秒GC5次,YGC时间总耗时250ms, 一次YGC耗时50ms
- FGC统计: 30分钟GC1次,FGC耗时125ms
二、JVM启动参数配置
-Xms12g, -Xmx12g, -XX:NewRatio=1, -Dport=80, -verbose:gc, -XX:MetaspaceSize=512m,
-XX:MaxMetaspaceSize=512m, -XX:+UseConcMarkSweepGC, -XX:+UseParNewGC,
-XX:+CMSParallelRemarkEnabled, -XX:+CMSScavengeBeforeRemark, -XX:CMSInitiatingOccupancyFraction=75, -XX:+PrintGCDetails,
-XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=/data/logs/
- -Xms12g:JVM启动时的初始堆大小为12GB。
- -Xmx12g:JVM堆的最大大小为12GB。
- -XX:NewRatio =1:新生代(Young Generation)与老年代(Old Generation)的比例为1:1。即新生代和老年代各占堆内存的一半。
- -Dport=80:设置系统属性
port的值为80,通常用于应用程序监听80端口。 - -verbose:gc:启用垃圾回收的详细日志输出。
- -XX:MetaspaceSize =512m:设置元空间(Metaspace)的初始大小为512MB。
- -XX:MaxMetaspaceSize =512m:设置元空间的最大大小为512MB。
- -XX:+UseConcMarkSweepGC:启用并发标记清除(CMS)垃圾回收器,用于老年代的垃圾回收。
- -XX:+UseParNewGC:启用并行新生代垃圾回收器(ParNew),与CMS配合使用。
- -XX:+CMSParallelRemarkEnabled:启用CMS垃圾回收器的并行标记阶段,以减少停顿时间。
- -XX:+CMSScavengeBeforeRemark:在CMS的重新标记阶段之前进行一次新生代的垃圾回收,以减少重新标记阶段的工作量。
- -XX:CMSInitiatingOccupancyFraction =75:设置CMS垃圾回收器在老年代占用率达到75%时启动垃圾回收。
- -XX:+PrintGCDetails:打印详细的垃圾回收信息。
- -XX:+HeapDumpOnOutOfMemoryError:在发生内存溢出(OutOfMemoryError)时生成堆转储文件。
- -XX:HeapDumpPath =/data/logs/:指定堆转储文件的存储路径为
/data/logs/。
-XX:NewRatio
- 新生代:用于存放新创建的对象,通常对象的生命周期较短。新生代又分为 Eden 区和两个 Survivor 区(From 和 To)。在新生代中,新创建的对象首先被分配到 Eden 区。当 Eden 区满了时,会触发 Minor GC,将存活的对象移动到 Survivor 区。新生代 Eden 区和 Survivor 区的比例:默认为 8:1:1。
- 老年代:用于存放经过多次垃圾回收后仍然存活的对象,通常对象的生命周期较长。对象晋升到老年代的默认次数:经过 15 次 Minor GC 后仍然存活。
-XX:MetaspaceSize =512m
- 元空间是 Java 8 引入的一种内存区域,用于存储类的元数据信息,例如类的结构、方法、字段等。
-XX:+UseConcMarkSweepGC
CMS 垃圾回收器是一种专注于减少垃圾回收停顿时间的回收器,适用于对响应时间要求较高的应用场景。
CMS 垃圾回收器主要针对老年代,采用“标记-清除”算法,整个回收过程分为以下几个阶段:
- 初始标记(Initial Marking):在初始标记阶段,JVM 会暂停所有用户线程(STW),标记出直接被 GC Roots 引用的对象。这一阶段速度较快,因为仅标记直接关联的对象。
- 并发标记(Concurrent Marking):并发标记阶段是 CMS 的核心,它从 GC Roots 开始遍历整个对象图,标记出所有存活的对象。此阶段用户线程和垃圾回收线程可以并发执行,因此不会暂停用户线程。
- 重新标记(Remark):由于并发标记阶段用户线程仍在运行,可能会导致对象引用关系发生变化。因此,重新标记阶段会修正并发标记期间发生变化的对象引用。这一阶段也会暂停用户线程,但时间较短。
- 并发清除(Concurrent Sweep):在并发清除阶段,垃圾回收线程会清除那些未被标记的对象,释放内存空间。此阶段同样与用户线程并发执行。
CMS 的优点
- 低停顿时间:通过并发执行标记和清除操作,显著减少了垃圾回收对用户线程的停顿时间。
- 适合交互式应用:适用于对响应时间要求较高的场景,如 Web 应用。
CMS 的缺点
- 内存碎片:由于采用标记-清除算法,可能会产生内存碎片,导致在分配大对象时因无法找到连续内存空间而提前触发 Full GC。
- 对 CPU 敏感:并发执行会占用 CPU 资源,可能导致在 CPU 资源紧张时性能下降。
- 无法处理浮动垃圾:并发清除阶段可能会遗漏一些在标记阶段之后新产生的垃圾对象。
-XX:+UseParNewGC
-XX:+UseParNewGC 是一个 JVM 参数,用于启用 ParNew 垃圾回收器。ParNew 是一种并行的新生代垃圾回收器,专门用于处理新生代的垃圾回收。它是基于 Stop-the-World (STW) 机制的,但通过并行执行来提高垃圾回收的效率。
ParNew 垃圾回收器的特点
-
并行回收:
- ParNew 是一种并行垃圾回收器,它利用多线程来加速新生代的垃圾回收过程。它会根据系统的 CPU 核心数或用户指定的线程数(通过
-XX:ParallelGCThreads参数设置)来并行执行垃圾回收任务。 - 并行执行可以显著减少垃圾回收的停顿时间,尤其是在多核处理器上。
- ParNew 是一种并行垃圾回收器,它利用多线程来加速新生代的垃圾回收过程。它会根据系统的 CPU 核心数或用户指定的线程数(通过
-
基于标记-复制算法:
- ParNew 使用 标记-复制(Mark-Copy) 算法来回收新生代的内存。新生代被划分为 Eden 区和两个 Survivor 区(From 和 To)。
- 在垃圾回收时,ParNew 会标记 Eden 区和 From Survivor 区中存活的对象,并将它们复制到 To Survivor 区。这种算法可以有效避免内存碎片问题。
-
与 CMS 垃圾回收器配合使用:
- ParNew 常常与 CMS(并发标记清除)垃圾回收器配合使用。在这种情况下,ParNew 负责新生代的垃圾回收,而 CMS 负责老年代的垃圾回收。
- 这种组合可以充分发挥 ParNew 的并行优势和 CMS 的低停顿优势。
使用场景
- 适合多核处理器:ParNew 非常适合在多核处理器上运行,因为它可以通过并行执行充分利用多核 CPU 的计算能力。
- 适合新生代频繁回收:如果应用中新生代的垃圾回收非常频繁,ParNew 可以显著提高垃圾回收效率,减少停顿时间。
- 与 CMS 配合:在需要低延迟的场景中,ParNew 和 CMS 的组合可以提供较好的性能表现。
优点
- 并行回收:通过多线程并行执行,显著减少了垃圾回收的停顿时间。
- 高效处理新生代:基于标记-复制算法,避免了内存碎片问题,同时提高了新生代的回收效率。
- 与 CMS 配合良好:可以与 CMS 垃圾回收器配合使用,实现新生代和老年代的高效回收。
缺点
- 对 CPU 资源敏感:并行执行会占用较多的 CPU 资源,可能导致在 CPU 资源紧张时对应用性能产生一定影响。
- 仅适用于新生代:ParNew 仅用于新生代的垃圾回收,老年代的垃圾回收需要配合其他回收器(如 CMS 或 G1)。
注意事项
- ParNew 垃圾回收器在 JDK 9 及之后的版本中被废弃,取而代之的是 G1 垃圾回收器。G1 是一种更加先进的垃圾回收器,支持新生代和老年代的统一回收,并且在低延迟和高吞吐量方面表现更好。
- 如果你正在使用 JDK 9 或更高版本,建议优先考虑使用 G1 垃圾回收器(通过
-XX:+UseG1GC参数启用)。
-XX:+CMSParallelRemarkEnabled
-XX:+CMSParallelRemarkEnabled 是一个 JVM 参数,用于优化 CMS(Concurrent Mark-Sweep)垃圾回收器的性能。具体来说,它允许在 CMS 的 重新标记(Remark)阶段 使用并行线程来执行标记操作。
在 CMS 的工作流程中,重新标记阶段 是一个 Stop-the-World (STW) 事件,意味着它会暂停所有用户线程。由于重新标记阶段需要扫描整个堆(包括老年代和新生代),因此可能会导致较长的停顿时间。
启用 -XX:+CMSParallelRemarkEnabled 参数后,JVM 会在重新标记阶段使用多个线程并行执行标记操作,从而显著减少停顿时间。这在多核处理器环境中尤其有效,因为并行执行可以充分利用多核 CPU 的计算能力。
从 JDK 1.8 开始,-XX:+CMSParallelRemarkEnabled 参数默认是启用的。
-XX:+CMSScavengeBeforeRemark
-XX:+CMSScavengeBeforeRemark 是一个用于优化 CMS(Concurrent Mark-Sweep)垃圾回收器性能的 JVM 参数。它的作用是在 CMS 的 Remark 阶段 之前强制执行一次 Minor GC(新生代垃圾回收),以减少老年代对新生代的跨代引用,从而降低 Remark 阶段的停顿时间。
作用和优化原理
-
背景:
- CMS 的 Remark 阶段是一个 Stop-the-World (STW) 事件,通常会扫描整个堆(包括老年代和新生代),因此可能导致较长的停顿时间。
- 如果新生代中存在大量对象,Remark 阶段的扫描时间会显著增加。
-
优化策略:
- 在 Remark 阶段之前执行一次 Minor GC,可以清理掉新生代中大部分短生命周期的对象,减少老年代对新生代的引用数量。
- 这样可以显著降低 Remark 阶段需要扫描的对象数量,从而减少停顿时间。
使用场景和效果
-
适用场景:
- 当应用的新生代对象生命周期较短,且 Remark 阶段的停顿时间较长时,启用该参数可以有效优化性能。
- 特别适用于高并发、低延迟的应用场景。
-
优化效果:
- 根据实际案例,启用
-XX:+CMSScavengeBeforeRemark后,单次执行时间超过 200ms 的 GC 停顿消失,GC 时间与业务波动保持一致。 - 在某些测试中,Final Remark 的平均耗时从 495ms 降低到 50ms 左右。
- 根据实际案例,启用
注意事项
- 额外停顿:虽然该参数可以减少 Remark 阶段的停顿时间,但它会引入一次额外的 Minor GC 停顿。因此,需要权衡 Minor GC 的停顿时间和 Remark 阶段的优化效果。
- 适用性:该参数并非在所有场景下都适用。如果新生代对象生命周期较长,或者新生代占用率较低,启用该参数可能不会带来显著的优化效果。
-XX:CMSInitiatingOccupancyFraction =75
-XX:CMSInitiatingOccupancyFraction=75 是一个用于优化 CMS(Concurrent Mark-Sweep)垃圾回收器的 JVM 参数,表示当老年代的内存占用率达到 75% 时,触发 CMS 垃圾回收。
参数作用
-
控制 CMS 回收的触发时机
默认情况下,CMS 垃圾回收器会在老年代内存占用率达到一定阈值时启动。通过设置-XX:CMSInitiatingOccupancyFraction=75,可以将触发阈值设置为 75%,即当老年代的内存占用率达到 75% 时,CMS 回收器开始工作。 -
避免频繁 Full GC
设置合适的阈值可以避免老年代内存耗尽导致的 Full GC,从而减少长时间的停顿。如果阈值设置过高(如接近 100%),可能会导致 CMS 回收器启动过晚,从而引发 Full GC。 -
预留空间
设置为 75% 意味着老年代会预留 25% 的空间,这为并发回收期间的内存分配提供了缓冲,避免因内存不足导致的并发模式失败。
配合其他参数
-XX:+UseCMSInitiatingOccupancyOnly:此参数确保 CMS 回收器始终基于设置的阈值(如 75%)触发,而不是由 JVM 自动调整。-XX:+CMSClassUnloadingEnabled:开启对永久代或元空间的垃圾回收,避免因元空间耗尽导致的 Full GC。
优化建议
- 合理设置阈值:根据应用的老年代内存使用情况,调整阈值以避免频繁触发 CMS 回收。例如,如果老年代常驻内存较大,可以适当提高阈值。
- 监控内存使用:通过工具(如
jstat或 GC 日志)监控老年代的内存使用情况,动态调整阈值。
JVM 内存占用和GC分析
YGC
- YGC就是上面提到的MinorGC的频率较高(6秒一次),说明新生代的对象分配速度较快,可能是由于应用程序创建了大量短期对象。
- 每次YGC耗时50ms,属于正常范围(通常YGC耗时在几十毫秒内是可以接受的)。
- 如果YGC的频率对应用程序的性能影响较大(例如导致明显的延迟或吞吐量下降),可以考虑增大新生代的大小,以减少YGC的频率。可以通过调整
-XX:NewRatio参数(例如设置为0.5)来增加新生代的比例。
FGC
- FGC的频率较低(30分钟一次),说明老年代的对象晋升速度较慢,整体内存使用较为健康。
- 每次FGC耗时125ms,对于CMS垃圾回收器来说,这个耗时是合理的(CMS的目标是减少停顿时间)。
- 如果FGC的频率和耗时对应用程序没有明显影响,则可以认为当前配置是合理的。
堆内存配置
- 堆内存大小(12GB)对于大多数应用来说是足够的,但如果YGC频率较高,可以尝试增大堆内存(例如增加到16GB或24GB),以减少GC的频率。
-XX:NewRatio=1的配置使得新生代和老年代各占一半堆内存(6GB)。如果YGC频率较高,可以尝试增大新生代的比例(例如设置-XX:NewRatio=0.5,新生代占2/3,老年代占1/3),以减少YGC的频率。