为什么这个Java进程不释放内存?

7
我编写了一个Java应用程序并在Fedora 24下运行它的Java进程。然后我检查了jconsole,并发现它使用约5到10兆字节的内存,图形上也可以看到垃圾回收的效果。
这是截图: jconsole screenshot 然后我检查了我的系统监视器,并发现相同的进程ID已经使用了超过100兆字节的内存。
这是截图: System Monitor screenshot 请告诉我为什么进程不释放未使用的内存? 有没有办法释放掉它?

1
这是什么Java应用程序?SWING? - Coderino Javarino
你为什么在意呢?这是针对Intellij的一个非常大的基于Java的程序。你并没有抱怨Chrome至少在两个进程中占用了超过100MB的内存。100MB并不是很多 - 你的Gnome进程将会占用更多的内存。如果你真的有问题,请告诉我们。100MB的内存现在已经算不了什么了。 - stdunbar
2
@stdunbar 我很在意,因为我在Mono和.NET中从未遇到过这种问题。我知道100 MB微不足道,但我提出这个问题是为了增加我的知识而不是为了遇到问题。 - Matin Lotfaliee
2
你正在查看IntelliJ的内存,而不是你的进程本身的内存。请使用Java命令在命令行上运行程序,因为从IntelliJ中分离出你的进程及其内存消耗将非常困难。 - stdunbar
您需要提供您的JRE版本和命令行标志(例如GC选项),因为这种行为在不同的JVM中是不同的。 - Daniel Pryden
显示剩余2条评论
2个回答

8
你的图表中蓝色线条代表已使用堆的大小,而分配堆的大小则没有显示出来。分配堆的大小通常比已使用堆的大小大得多,这样JVM可以分配更多对象而不会因为空间不足而返回操作系统获取更多内存(这很昂贵)。在你的情况下,系统显示的100mb中有一部分是JVM本身,但大部分可能是未使用的分配堆。

当你运行一个Java程序时,如果没有指定堆大小,JVM将根据你的计算机、操作系统、JVM版本等信息尝试确定一个合适的设置。当我在我的16GB RAM和Java 8上运行一个简单的Hello World程序时,它最初为堆分配了256mb。这显然远远超出了它的需求!如果我想强制它使用更少的内存,我可以使用-Xms命令行来设置初始堆分配,-Xmx用于设置允许的最大值。我的猜测是,如果你设置类似-Xms20m的值,你的进程将使用更少的内存。在IntelliJ中,将该设置添加到运行配置的VM Options字段中即可。


3
系统监视器中报告的内存是进程使用的所有内存,而不仅仅是Java堆。这个内存包括:
- 虚拟机本身的可执行文件和它加载的库 - 作为进程的虚拟机的工作空间,用于诸如Hotspot编译器、GC、IO缓冲区、屏幕和图形缓冲区、读取VM文件等 - Java堆和其他可报告的内存结构
在您的情况下,该进程使用10MB的内存来存储Java堆栈和Java对象。另外的90MB是Java程序本身和虚拟机内部的内存。
这是简短的答案,但还有一个重要的考虑因素 - Java可以(并且确实)将多余的堆释放回操作系统。这由`-XX:MinHeapFreeRatio`和`-XX:MaxHeapFreeRatio`标志控制。默认情况下,MaxHeapFreeRatio为70%,这几乎与您的堆图表中显示的内容相同(从6MB到略低于10MB的锯齿形图案)。如果您的应用程序有更大的降幅,则会在系统监视器中看到Java进程的(小)锯齿形图案。
为了提高性能,通常应允许JVM保留从GC中释放的大块堆。为什么?因为我们知道JVM将立即需要开始分配内存,让Java保持这种方式对进程(和操作系统)更有效率。
因此,总结如下:
- 系统监视器显示整个JVM进程使用的内存 - Java堆只是使用进程内存的一项 - 通常情况下,JVM应保留(至少部分)在GC后释放的堆,因为我们几乎肯定会在接下来的几秒钟内使用它 - 在本例中,Java堆处于正常范围内振荡,并且在这种情况下默认的内存配置允许Java在GC后保留所有多余的堆

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