2

我在 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 堆内?

4

3 回答 3

5

考虑什么构成了 Java 进程。

你有:

  • JVM(一个 C 程序)
  • JNI 数据
  • Java字节码
  • Java 数据

值得注意的是,它们都存在于 C 堆中(JVM 堆自然是 C 堆的一部分)。

在 Java 堆中只是 Java 字节码和 Java 数据。但是 Java 堆中还有“空闲空间”。

典型的(即 Sun)JVM 只在必要时增加它的 Java Heap,但从不缩小它。一旦它达到其定义的最大值 (-Xmx512M),它就会停止增长并处理剩下的任何东西。当最大堆用完时,您会收到 OutOfMemory 异常。

Xmx512M 选项不做的是限制进程的整体大小。它仅限制进程的 Java Heap 部分。

例如,您可能有一个人为的 Java 程序,它使用 10MB 的 Java 堆,但调用一个分配 500MB 的 C 堆的 JNI 调用。您可以看到您的进程大小如何,即使 Java 堆很小。此外,使用新的 NIO 库,您也可以将内存附加到堆外。

您必须考虑的另一个方面是 Java GC 通常是“复制收集器”。这意味着它从正在收集的内存中获取“实时”数据,并将其复制到内存的不同部分。复制到的这个空白空间不是堆的一部分,至少在 Xmx 参数方面不是。它就像“新堆”,在复制后成为堆的一部分(旧空间用于下一次 GC)。如果你有一个 512MB 的堆,它是 510MB,Java 将把实时数据复制到某个地方。天真的想法会是另一个大的开放空间(比如 500+MB)。如果你所有的数据都是“活的”,那么它需要一个像这样的大块来复制。

因此,您可以看到,在最极端的情况下,您需要至少双倍系统上的可用内存来处理特定的堆大小。512MB 堆至少需要 1GB。

事实证明,实际情况并非如此,内存分配等比这更复杂,但您确实需要大量空闲内存来处理堆副本,这会影响整个进程的大小。

最后,请注意 JVM 会做一些有趣的事情,例如将 rt.jar 类映射到 VM 以简化启动。它们映射在只读块中,并且可以在其他 Java 进程之间共享。这些共享页面将对所有 Java 进程“计数”,即使它实际上只消耗一次物理内存(虚拟内存的魔力)。

现在至于为什么您的进程继续增长,如果您从未遇到 Java OOM 消息,这意味着您的泄漏不在 Java 堆中,但这并不意味着它可能不在其他地方(JRE 运行时,a 3rd 方 JNI 库、本机 JDBC 驱动程序等)。

于 2008-09-17T00:22:02.673 回答
3

一般来说,只有 Java 对象中的数据存储在 Java 堆上,Java VM 所需的所有其他内存都是从“本机”或“C”堆中分配的(实际上,Java 堆本身只是分配的一个连续块来自 C 堆)。

由于 JVM 要求 Java 堆(或使用分代垃圾收集的堆)是一块连续的内存,因此通常在 JVM 启动时分配整个最大堆大小(-mx 值)。在实践中,Java VM 将尝试最小化其对该空间的使用,以便操作系统不需要为其保留任何实际内存(操作系统足够精明,可以知道何时从未写入过存储空间) .

因此,Java 堆会在内存中占据一定的空间。

剩余的存储空间将由 Java VM 和正在使用的任何 JNI 代码使用。例如,JVM 需要内存来存储来自已加载类的 Java 字节码和常量池、JIT 编译代码的结果、编译 JIT 代码的工作区、本机线程堆栈和其他此类杂项。

JNI 代码只是特定于平台的(编译的)C 代码,可以以“本机”方法的形式绑定到 Java 对象。当这个方法被执行时,绑定的代码被执行,并且可以使用标准的 C 例程(例如 malloc)分配内存,这将消耗 C 堆上的内存。

于 2008-09-17T00:06:15.333 回答
1

我对您给出的数字的唯一猜测是 Java VM 中的内存泄漏。您可能想尝试他们在您提到的论文中列出的其他 VM 之一。另一种(更困难的)替代方法可能是在 HP 平台上编译 open java。

Sun 的 Java 还没有 100% 开放,他们正在努力,但我相信 sourceforge 中有一个。

顺便说一下,Java 也会破坏内存。有时它会稍微混淆操作系统内存管理(当 windows 内存不足并要求 Java 释放一些内存时,你会看到它,Java 会触及它的所有对象,导致它们从交换文件中加载,windows 痛苦地尖叫并死掉),但是我不认为这就是你所看到的。

于 2008-09-16T23:55:23.080 回答