某店,5月31日18:20左右卡顿,超时弹框。 查看gc日志,发现gc了90秒。 门店32G内存,只分配了4G内存,暂时让调大内存。 一天只fullGc几次,大部分在5秒左右,还可以接受。
2024-05-31T18:17:24.721+0800: [GC2024-05-31T18:17:24.721+0800: [ParNew: 1790256K->209664K(1887488K), 0.1562262 secs] 3709497K->2167713K(3984640K), 0.1563877 secs] [Times: user=1.42 sys=0.00, real=0.16 secs]
2024-05-31T18:17:24.879+0800: [GC [1 CMS-initial-mark: 1958049K(2097152K)] 2167849K(3984640K), 0.1631782 secs] [Times: user=0.17 sys=0.00, real=0.16 secs]
2024-05-31T18:17:25.552+0800: [GC2024-05-31T18:17:25.552+0800: [ParNew (promotion failed): 1887488K->1854935K(1887488K), 84.0805089 secs]2024-05-31T18:18:49.632+0800: [CMS2024-05-31T18:18:50.427+0800: [CMS-concurrent-mark: 1.301/85.386 secs] [Times: user=109.44 sys=14.70, real=85.39 secs] (concurrent mode failure): 2069394K->1241702K(2097152K), 6.9313568 secs] 3845537K->1241702K(3984640K), [CMS Perm : 287367K->274862K(607820K)], 91.0120818 secs] [Times: user=110.56 sys=14.69, real=91.01 secs]
2024-05-31T18:18:57.116+0800: [GC2024-05-31T18:18:57.116+0800: [ParNew: 1677824K->46746K(1887488K), 0.0254629 secs] 2919526K->1288449K(3984640K), 0.0257600 secs] [Times: user=0.28 sys=0.00, real=0.03 secs]
老年代1958049K,快满了,所以进行CMS回收。年轻代有对象需要晋升到老年代,结果放不下,报promotion failed晋升失败。
老年代正在cms回收,新对象要放进来,结果放不下,报错concurrent mode failure。
结果cms降级为单线程的fullGC,耗时90秒。
根源还是请求,查数据太多,又大又慢,年轻代gc不掉,晋升到老年代。
老年代cms回收,碎片化是不可避免的,最终导致fullGC。
CMS缺点以及本身机制存在的问题,所以JVM重新实现了G1的垃圾回收器去替换CMS垃圾回收器,在JDK14版本彻底的删除了CMS!
看了半天,大概解决方法是: -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=75 默认值是68(注意,JDK6之后变成92了!!),这个可以根据实际调优目标来调整,这个参数就比较应开始提到的,调优目标是降低延迟还是提高吞吐,如果是为了降低单次GC延迟,那么这个值阔以再往低了调一些,不过调的太高可能导致老年代剩余空间不够招呼并发收集产生的浮动垃圾而频繁的触发Full GC。
-XX:+UseCMSInitiatingOccupancyOnly 使用CMS的话这个参数一定要加上,一定要加上,一定要加上,重要的事情说三遍,否则虚拟机后面还是会自作聪明的自己计算上个参数的比值。
但是,碎片还是没辙吧,终极办法还是换G1GC回收方式。
参考: https://juejin.cn/post/6986538910222385160 https://www.sczyh30.com/posts/Java/jvm-gc-hotspot-implements/
|