G1GC老年代堆空间不断增长,使用量保持恒定 - 导致伊甸园区域饿死。

6
老年代已分配堆随着时间的推移而增加(在生产环境中大约为5至6天),但是老年代已使用堆并没有增加。伊甸园和幸存者堆被强制减少到最小值(总堆大小的5%),所以垃圾收集会变得越来越频繁。应用程序从一开始就缓存了一个大对象图,然后在其运行期间有其他时间/使用限制的缓存。它具有相当高的对象创建速率,但除了缓存的对象之外,很少将其提升到老年代。
我已经通过gceasy.io运行了GC日志,并且可以看到内存的上述行为: https://gceasy.io/my-gc-report.jsp?p=c2hhcmVkLzIwMjAvMDUvMTEvLS1nY2xvZy50YXIuZ3otLTExLTMwLTE5&channel=WEB.

gclog: https://drive.google.com/open?id=176X-Lku4D3DGCCdTiB0_z545N8n0tfKc

本次运行的Grafana内存指标https://snapshot.raintank.io/dashboard/snapshot/k6g3ljG7cQUEJM7jA4c5tBK1dsUnzabd

运行结束时的堆转储(负载已经移除了大约一个小时,这是一个500M的gz文件):https://drive.google.com/open?id=14ghzIVnpelInSyQBhCwUwM5VkuOjX13-

我们似乎没有高数量的巨型对象创建。服务器有12G的RAM,堆有6G。

JVM:

openjdk version "1.8.0_242"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.242-b08, mixed mode)

虚拟机标志:

-XX:CICompilerCount=4
-XX:ConcGCThreads=2
-XX:G1HeapRegionSize=2097152
-XX:GCLogFileSize=104857600
-XX:InitialHeapSize=6442450944
-XX:InitialRAMPercentage=50.000000
-XX:+ManagementServer
-XX:MarkStackSize=4194304
-XX:MaxHeapSize=6442450944
-XX:MaxNewSize=3865051136
-XX:MaxRAMPercentage=50.000000
-XX:MinHeapDeltaBytes=2097152
-XX:MinRAMPercentage=50.000000
-XX:NumberOfGCLogFiles=10
-XX:+PrintGC
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseG1GC
-XX:+UseGCLogFileRotation

我们使用基于CentOS的Openshift运行: CentOS Linux release 7.7.1908 (Core) 内核版本:3.10.0-1062.12.1.el7.x86_64

你尝试过在程序运行初期进行堆转储和分析,然后再过几天进行比较吗? - kenny_k
1
我已经尝试过了,没有看到什么特别的地方,特别是实际使用的堆总是比承诺的堆要低得多。我有一个堆转储文件,显示总共使用了1.3G的堆,但旧一代已经承诺接近6G(我会上传一个链接)。因此伊甸园饥饿了。我已经上传了一个Grafana指标链接,显示内存使用情况,类似于gceasy所显示的。 - Tom Dearman
@TomDearman我也遇到了同样的问题(提交了~ 2 *使用),你找到根本原因了吗? 当我有一个很小的eden +幸存者承诺空间时,gc将使用大部分CPU使用率,并且整个应用程序即使我有足够的内存也会变慢。 - Hamdi
2个回答

1

从您的GC日志中,下面是在GCViewer中的查看方式

GC日志显示引用处理是高GC时间的瓶颈 建议尝试-XX:+ParallelRefProcEnabled,看看是否有所帮助

  [Ref Proc: 151.3 ms]
  [Ref Proc: 147.2 ms]
  [Ref Proc: 146.6 ms]
  [Ref Proc: 183.0 ms]
  [Ref Proc: 156.4 ms]
  [Ref Proc: 152.7 ms]
  [Ref Proc: 143.7 ms]
  [Ref Proc: 137.8 ms]
  [Ref Proc: 194.8 ms]
  [Ref Proc: 153.3 ms]
  [Ref Proc: 153.6 ms]

还有一些其他的尝试可以进行以改进:

  1. 打开-XX:PrintReferenceGC以获取有关引用处理时间的更多详细信息
  2. 逐渐减少-XX:InitiatingHeapOccupancyPercent(默认值为45),看看是否可以更早地发生混合GC并帮助改善行为
  3. 增加-XX:G1NewSizePercent以避免年轻代缩小过多

希望能对此有所帮助。

GC Viewer UI


0

一般来说,committed内存值高于used是正常的。 committed-Xms开始,最高可以达到-Xmx,但committed并不是residentResident在这里表示在内存中,而used = resident + swapped pages。因此,used可能会波动很大,而committed则不那么大,至少这是我对它的理解。

当GC启动时,将以-Xms的值作为初始committed内存,并逐渐增长(当然最高不超过-Xmx)。现在我已经介绍完了,我真的不认为这与您的应用程序有任何意义。让我解释一下。

从您提供的日志中,我所能看到的是一切都很正常(和预期)。G1有一个默认值为MaxGCPauseMillis = 200ms的参数,它指示您的应用程序允许停止的时间(在正常情况下)。根据这个值,G1会做出适当的决策来调整您的区域大小。根据您的日志,平均而言,大约有270MBEden空间可以在最多0.2s内进行收集。以下是您日志中的一个随机示例:

[Eden: 280.0M ....
Times: user=0.49 sys=0.00, real=0.18 secs
2020-05-04T02:43:01.742+0000: 315128.451: [GC pause (G1 Evacuation Pause) (young), 0.1740299 secs]

所以这正是你间接要求的。对我来说,你的应用程序完全没问题,那些GC暂停(G1疏散暂停)需要和你配置的一样多。顺便说一下,你很幸运吗?但在整个日志文件中,我没有发现一个Full GC(除了你拍摄堆转储时的那个)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接