Java 本机内存跟踪堆提交的数量远远超过堆转储的总数

Posted

技术标签:

【中文标题】Java 本机内存跟踪堆提交的数量远远超过堆转储的总数【英文标题】:Java native memory tracking heap committed much more than total from heap dump 【发布时间】:2020-01-29 10:26:47 【问题描述】:

使用jdk1.8.0_152我正在尝试追踪我的java程序的哪一部分使用的内存最多(主要在堆中)

使用top我看到整个过程使用了大约1.109G的剩余内存

使用jcmd PID VM.native_memory 我看到保留的总大小为 4704896 KB,提交的大小约为 1290820 KB。 已提交的内存略多于剩余内存,但我读过并非所有已提交的内存都可以分页到实际内存,所以我不太担心这种差异

我现在主要关心的是VM.native_memory 的堆内存使用量与我使用jcmd PID GC.class_histogram 时的总堆内存使用量之间的差异

我还尝试使用jstat -gc PID 比较堆使用情况,得到的结果类似于GC.class_histogram

根据GC.class_histogramjstat -gc,堆使用量约为250MB,但使用VM.native_memory 堆使用量(在Java Heap 部分中提交)约为1000000KB(小于1GB)但实际的RSS 内存似乎更接近VM.native_memory中的承诺总数

我现在的猜测是 VM.native_memory Java Heap 包含尚未被垃圾回收的内存,但即使我运行垃圾回收,我看到 jstat -gc 的结果急剧下降,而 VM.native_memory 根本不受影响(虽然我听说用户手动调用垃圾收集并不总是会导致完整的垃圾收集,但至少 jstat -gc 似乎与 GC.class_histogram 的结果相匹配。

我听说的另一件事是top 的剩余内存在使用内存的进程释放它时并不总是被释放,直到绝对需要释放该内存。

总结一下

    为什么VM.native_memory 显示的堆内存使用量与jstatGC.class_histogram 不同? 我应该使用哪个指标来确定我的 java 进程正在使用多少内存? (鉴于top 中的剩余内存可能并不总是反映实际使用情况)

【问题讨论】:

@Holger 根据docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/… 本地内存似乎是指 Java Hotspot VM 使用的所有内存,包括堆以及其他内存,例如线程(堆栈)、类缓存等 我明白了,所以你不明白“提交”并不意味着“用 Java 对象填充”意义上的“使用”? @Holger 嗯.. 是的,如果我理解正确的话?但根据文档,它说“请注意,实际上只使用了提交的内存。”所以看起来应该“使用”已提交。根据总承诺与top 所说的进程正在使用的相似程度,看起来应该“使用”已提交? (虽然剩余内存可能是指 jvm 已锁定的总内存与实际“充满对象”的内存相比。但根据文档,“提交”似乎是指实际填充该内存的对象“) 尚未提交的内存,显然没有被使用。但是,已经使用但由于垃圾回收而变得空闲的堆内存仍然被提交,并将被重用于后续分配。将内存回馈给系统的情况很少发生,一些垃圾收集器永远不会回馈。 已提交的 RAM 不一定是上限,必要时可能会尝试提交更多内容(不过,通常在尝试提交更多内容之前至少会尝试一次垃圾收集)。另请参阅this summary。 【参考方案1】:

我猜你的 jvm 堆已经设置了一个范围大小。

本机内存跟踪器从 os 视图显示内存大小(接近于top 命令),而jstat 从 jvm 内部视图显示。

这意味着jvm会向os请求mem(检查来自top),但真正使用与否取决于它自己(检查jstatjmap)。

【讨论】:

以上是关于Java 本机内存跟踪堆提交的数量远远超过堆转储的总数的主要内容,如果未能解决你的问题,请参考以下文章

JVM中的本机内存跟踪

分析巨大的 C# 堆转储(超过 10 GB)

lambda 表达式的 Java 堆转储分析

分析大型 Java 堆转储 - 内存错误

java堆转储文件太大打不开怎么办

在大型 Java 堆转储中查找内存泄漏的方法