为什么Java运行垃圾回收器有时会增加保留的内存?

3
我们有一个运行在Tomcat8.5.47服务器上的Java8 Web应用程序。我们每次只有20-60个用户会话,但大部分时间上传文件大小高达600MB。我们还使用Hibernate和C3P0来管理数据库连接。
我们监控了几天服务器,发现有时候Java保留的RAM突然增加,垃圾回收器没有释放它。我们如何管理这个问题?是否有任何方法可以释放保留的RAM并防止Tomcat增加RAM?还有降低任务管理器中使用的RAM的方法吗?
以下是我们的设置:
-XX:MaxPermSize=1g  -XX:+UseG1GC  -XX:+UseStringDeduplication  -XX:MaxHeapFreeRatio=15  -XX:MinHeapFreeRatio=5  -XX:-UseGCOverheadLimit  -Xmn1g  -XX:+UseCompressedOops  -Xms10g  -Xmx56g

这是一个问题发生时的分析器图像: 在此输入图片描述

这是2小时后的分析器和任务管理器图像: 在此输入图片描述

附注:我们使用JProfiler进行分析,绿色表示保留的内存,蓝色表示已用内存。第二个框可以跟踪垃圾回收活动,第三个框用于类,第四个框显示线程活动,最后一个框用于CPU活动。
感谢大家的回答。


你确定任务管理器报告的是内存使用情况吗?比如常驻内存?我非常怀疑。 - Eugene
我不确定,但问题在于当任务管理器显示出56G的内存时,CPU使用率也会增加,我们会在服务器上检测到减速。这些数量每小时增加是正常的吗? - leila s
这里显然有什么我没有理解,但是如果你不想让JVM分配56GB的堆,为什么要告诉它可以这样做呢? - Neil Coffey
1
如果答案是“因为它需要一个带有20-60个用户的56GB堆”,我会首先尝试解决这个问题! - Neil Coffey
正如我在评论中所说,我们知道56g是巨大的,也知道有些地方出了问题,但我们必须这样做,因为当任务管理器上显示的内存量增加时,CPU使用率也会增加,我们会检测到速度变慢,直到重新启动Tomcat。因此,我们必须增加堆大小以推迟这些重启,并尝试解决问题。 - leila s
2个回答

2
这些类型的问题从来都不容易,主要是因为要回答得“正确”,提问者需要基本了解操作系统如何处理和管理内存;而且有不同类型的内存(至少包括常驻内存、已提交内存和保留内存)。我远远不够熟练,也无法完全正确回答这些问题,但我一直在学习并变得更加擅长。它们意义截然不同,其中一些通常是无关紧要的(我认为“保留内存”就是这样的一个例子)。由于你正在使用Windows操作系统,所以我建议你先观看this, imho
在观看完这个之后,你需要转向JVM世界并了解JVM进程。堆由垃圾收集器管理,因此要缩小一些未使用的堆 - 垃圾收集器需要能够做到这一点。而在jdk-12之前,G1可以做到这一点,但它从来不是非常热衷于此。自jdk-12以来,有一个JEP将返回内存,即:它将内存“取消提交”。但请确保阅读何时发生这种情况。还要注意,其他收集器如Shenandoah和/或ZGC更经常地执行此操作。
当然,如果你禁用了-UseGCOverheadLimit,你会看到CPU使用率飙升(垃圾回收线程疯狂运行以释放空间),而且所有操作都会变得缓慢。如果我是你,我会重新启用它,让垃圾回收失败,并分析垃圾回收日志以了解情况。对于20-60个用户来说,56GB的堆内存是一个巨大的数字(这肯定像是一个泄漏问题?)。请注意,如果没有垃圾回收日志,可能无法给出解决方案。
附注:请看一下你分享的第一个屏幕截图,注意那里有两种颜色:绿色和蓝色。我不知道那是什么工具,但绿色似乎是“保留内存”,蓝色是“已使用”(这是使用的含义)。但如果你能准确说明它们的含义,那就太好了。

是的,你说得对。我知道56G很大,但我们确实别无选择,以防止服务器变慢并需要每周或有时每4天重新启动Tomcat。我们不是选择Windows作为服务器,而是被迫这样做。此外,我们已经尽了很多努力来提高代码性能并选择正确的设置,但我们还需要更多的工作,并且我们知道这一点。如果您可以通过介绍有用的资源来帮助我们,我们将不胜感激。非常感谢。 - leila s
1
@leilas 我已经表达了我的想法 - 让它失败并生成堆转储。分析它在哪里以及为什么会失败,老实说我看不到其他的选择。 - Eugene

1
Java8即使JVM不需要内存,也不会将分配的RAM归还给操作系统。如果需要此功能,则需要切换到另一个版本的JDK。这是JEP https://openjdk.java.net/jeps/346 ,它表示该功能已在版本12中提供,因此我假设版本12之后的JDK应该具有该功能。
防止保留内存增加的唯一方法是减少Xmx值。由于您将其设置为56g,我假设您允许Tomcat最多使用56g的内存。因此,如果您认为这太多了,只需减少该数字即可。

1
  1. 当然,它会释放内存 (https://dev59.com/dLjna4cB1Zd3GeqP5CEo#59377080).
  2. "Java8不会将分配的RAM归还给操作系统" - 那么...一旦进程使某些内存驻留,它就永远不会将其归还吗?这当然是极不准确的(并且您也混淆了术语)。既然您已经链接了该JEP,您可以再次阅读它:...收集器可能不会返回已提交的Java堆内存....
  3. 防止增加保留内存的唯一方法-增加“保留内存”是完全可以的。
- Eugene

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