我在HP-UX环境中遇到了某些Java应用程序的特殊问题。
堆设置为-mx512,但是,使用gpm查看此java进程的内存区域,它使用超过1.6GB的RSS内存显示它,1.1GB分配给DATA区域。在24-48小时内迅速生长,然后大幅减速,每隔几小时仍然增长2MB。但是,Java堆没有泄漏迹象。
好奇这是怎么可能我研究了一下,发现这篇关于java堆和c堆内存泄漏的HP报告:http://docs.hp.com/en/JAVAPERFTUNE/Memory-Management.pdf
我的问题是什么决定了C堆与Java堆中运行的内容,对于那些没有通过java堆运行的东西,你如何识别在C堆上运行的那些对象?另外java堆是否位于C堆内?
答案 0 :(得分:4)
考虑构成Java进程的原因。
你有:
值得注意的是,它们都存在于C堆中(JVM堆自然是C堆的一部分)。
在Java堆中只是Java字节码和Java数据。但Java堆中的内容也是“自由空间”。
典型的(即Sun)JVM只在必要时增加它的Java堆,但从不缩小它。一旦达到其定义的最大值(-Xmx512M),它就会停止增长并处理剩下的任何内容。当最大堆耗尽时,您将获得OutOfMemory异常。
Xmx512M选项没有做什么,限制了整个过程的大小。它仅限制了流程的Java Heap部分。
例如,你可能有一个人为的Java程序使用10mb的Java堆,但是调用一个分配500MB C堆的JNI调用。即使Java堆很小,您也可以看到进程大小的大小。此外,使用新的NIO库,您也可以在堆外部附加内存。
您必须考虑的另一个方面是Java GC通常是“复制收集器”。这意味着它从它收集的内存中获取“实时”数据,并将其复制到内存的不同部分。这个空的空间复制到IS不是HEAP的一部分,至少不是Xmx参数。它就像“新堆”一样,并在复制后成为堆的一部分(旧空间用于下一个GC)。如果你有一个512MB的堆,并且它是510MB,那么Java将在某个地方复制实时数据。天真的想法是另一个大的开放空间(如500 + MB)。如果你的所有数据都是“实时”的,那么就需要一大块这样的数据来复制。
因此,您可以看到,在最极端的情况下,您需要至少两倍的系统可用内存来处理特定的堆大小。 512MB堆至少1GB。
事实证明,实际情况并非如此,内存分配等比这更复杂,但是你确实需要大量的可用内存来处理堆副本,这会影响整个进程的大小。
最后,请注意,JVM会执行有趣的操作,例如将rt.jar类映射到VM以简化启动。它们映射在只读块中,可以在其他Java进程中共享。这些共享页面将“计算”所有Java进程,即使它实际上只消耗一次物理内存(虚拟内存的魔力)。
现在,为什么你的进程继续增长,如果你从未点击过Java OOM消息,这意味着你的泄漏不在Java堆中,但这并不意味着它可能不在其他东西中(JRE)运行时,第三方JNI库,原生JDBC驱动程序等。)。
答案 1 :(得分:2)
通常,只有Java对象中的数据存储在Java堆中,Java VM所需的所有其他内存都是从“本机”或“C”堆中分配的(实际上,Java堆本身只是一个)从C堆分配的连续块。)
由于JVM要求Java堆(或者如果正在使用分代垃圾收集的堆)是一块连续的内存,整个最大堆大小(-mx值)通常在JVM开始时分配。在实践中,Java VM将尝试最小化其对此空间的使用,以便操作系统不需要为其保留任何实际内存(操作系统足够精确,可以知道从未写入存储的时间)
因此,Java堆将在内存中占用一定的空间。
Java VM和正在使用的任何JNI代码将使用其余存储。例如,JVM需要内存来存储来自加载类的Java字节码和常量池,JIT编译代码的结果,编译JIT代码的工作区域,本机线程堆栈和其他此类杂物。
JNI代码只是特定于平台的(已编译的)C代码,可以以“本机”方法的形式绑定到Java对象。执行此方法时,将执行绑定代码并使用标准C例程(例如malloc)分配内存,这将占用C堆上的内存。
答案 2 :(得分:0)
我唯一猜测的是你给出的数字是Java VM中的内存泄漏。您可能想尝试他们在您引用的论文中列出的其他VM之一。另一个(更难的)替代方案可能是在HP平台上编译开放的Java。
Sun的Java尚未100%开放,他们正在努力,但我相信sourceforge中有一个是。
Java也会让内存大打折扣。有时它会混淆操作系统内存管理一点(当Windows耗尽内存并要求Java释放一些时,你会看到它,Java触及所有它的对象导致它们从交换文件加载,窗口在痛苦中尖叫并死亡),但是我不认为这就是你所看到的。