JVM进程与JVM堆内存使用的区别

21

我阅读了进程内存 vs 堆 -- JVM,我也遇到同样的问题。

JVM进程内存使用量持续增加且不会缩减。我通过在Linux服务器上运行 top 命令来检查。该应用程序将作业调度到集群中(使用Quartz+Sun Java DRMAA API)。

Java堆空间在应用程序生命周期内保持在限制范围内,但JVM进程显示内存使用量稳步上升且没有下降。

这是内存泄漏吗?如果是,为什么堆空间保持在限制范围内。有人可以解释一下吗?

更新:我使用 -Xmx1600m -Xms1600m,当我通过jconsole跟踪时,可以看到堆空间远低于450m的这个限制,但 top 命令显示该进程正在使用超过900m的内存。


1
请提供更多细节...正在使用多少内存,最大的内存使用量是多少以及经过多长时间达到的,您正在使用哪些-X和相关的标志等等。 - jkraybill
你可以尝试一下使用 -XX:MaxHeapFreeRatio,看看是否能解决你的问题。 - trutheality
我正在研究一个类似的问题,其中“top”显示虚拟大小超过1GB,而实际大小不到50MB。因此,虚拟/实际比率非常高。使用jmap或“-XVerboseGC”开关的堆转储显示堆大小非常小(小于50MB)。我该如何找出占用非堆内存的原因?涉及使用NIO的套接字IO,但我需要证据并需要跟踪任何内存泄漏/查找根源。 - CruiZen
4个回答

17

总虚拟内存使用量是最大堆 + 线程栈 + 直接内存 + Perm Gen + 共享库的总和。这不会缩小。

实际主内存使用量取决于多少虚拟内存已被占用。共享库是共享的,因此有多个JVM不会导致此内存加倍等。

JVM从不释放内存到操作系统,但如果主内存长时间未被使用,则可以将其交换出去(如果需要)。


2
我在系统日志消息中看到以下错误,这基本上杀死了Java进程 Jul 18 04:55:31 seqwd5 kernel: Out of memory: Killed process 16333, UID 501, (java). Jul 18 04:55:31 seqwd5 kernel: java invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0 - Kathir
当你的内存和交换空间不足时,会发生此错误。我建议你增加你的交换空间,或最好是主内存。你可以尝试减少你使用的内存,因为看起来你没有1600M的可用空间。 - Peter Lawrey
@PeterLawrey请看一下我的问题,谢谢。https://dev59.com/qF0Z5IYBdhLWcg3wvSd2 - Daniel Newtown
2
只是一条注释,JVM 可以将内存释放回操作系统。请参考 Oracle 的页面。http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6498735。 - huican
@huican 这是可能的,但你无法控制它,而且很少发生。 - Peter Lawrey
显示剩余7条评论

2

实际内存消耗比你使用Xmx等设置的值更高,这是正常的。Java会为其他事物分配内存,包括每个线程的堆栈。虚拟机的总内存消耗超过-Xmx的值并不罕见。


1
参数 -Xmx1600m -Xms1600m 告诉 JVM 在最小和最大内存分别分配 1600MB 的内存。因此,JVM 应该在启动时分配 1600MB 的内存,并永远不会释放它。
如果您希望 JVM 将内存释放回操作系统,则 -Xms 应该尽可能低,并且您可能需要使用带有新 G1 垃圾收集器的 Java 1.7。stefankrause.net/wp/?p=14.
在使用 Mac OS X 10.8 和 Java 1.7 的情况下,使用 -Xms32m -Xmx256m -XX:+UseG1GC -XX:MinHeapFreeRatio=5 -XX:MaxHeapFreeRatio=10,内存在运行 System.gc() 后释放回操作系统。

1
这实际上是不正确的。如果没有使用,它会导致JVM分配完整的Xms 1.6GB,然后在第一个GC周期后释放几乎所有内存。请参见http://java-monitor.com/forum/showthread.php?t=427。 - Jim Mitchener
1
java-monitor.com/forum/showthread.php?t=427 这篇文章是关于测量堆内存使用情况的,而问题则是关于JVM正在使用的操作系统内存。 - Fireblaze

0

在堆中,Java虚拟机(JVM)存储Java应用程序创建的所有对象,例如使用“new”运算符。Java垃圾收集器(gc)可以逻辑上将堆分成不同的区域,以便gc可以更快地识别可以删除的对象。

新对象的内存是在运行时在堆上分配的。实例变量存在于声明它们的对象内部。

栈是存储方法调用和局部变量的地方。如果调用方法,则其堆栈帧被放置在调用堆栈的顶部。堆栈帧保存方法的状态,包括正在执行哪行代码以及所有局部变量的值。堆栈顶部的方法始终是该堆栈的当前运行方法。线程有自己的调用堆栈。

如前所述,在Java中,对象是在堆中创建的。编程语言不提供让程序员决定是否应在堆栈中生成对象的可能性。但在某些情况下,将对象分配到堆栈上是可取的,因为在堆栈上的内存分配比在堆上的内存分配更便宜,在堆栈上的释放是免费的,并且堆栈由运行时有效地管理。

JVM 内部因此使用逃逸分析来检查对象是否仅在一个线程或方法中使用。如果 JVM 确认了这一点,它可能会决定在堆栈上创建对象,从而提高 Java 程序的性能。(http://www.ibm.com/developerworks/java/library/j-nativememory-linux/)

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