JVM所消耗的本地内存 vs Java进程总内存使用量

6

我有一个小的Java控制台应用程序,希望在内存使用方面进行优化。它运行时Xmx设置为仅64MB。根据不同的监控工具(htop,ps,pmap,Dynatrace)显示的进程的整体内存使用量超过250MB。我主要在Ubuntu 18上运行它(也在其他操作系统上进行了测试)。

我使用了-XX:NativeMemoryTracking java参数和jcmd的本地内存跟踪来查找为什么会在堆之外使用如此多的内存。

当汇总NMT显示的值时,与htop显示的Resident Memory几乎相同。

NMT:

Total: reserved=1518873KB, committed=255877KB

htop

enter image description here

我使用了几个JVM参数来减少本地内存消耗(降低堆栈大小,将GC更改为串行,类数据共享等)。根据NMT,保留和已提交的内存指标(malloc和mmaped)总体上都下降了约50MB。

NMT

Total: reserved=1475110KB, committed=209218KB

我使用的所有工具(htop, ps, pmap, Dynatrace)都没有显示出任何区别。进程使用的总内存仍然是250MB。

  1. 问题是为什么?为什么将JVM使用的本地内存减少并没有对Java进程使用的驻留内存产生任何影响?它是否在预先保留某种方式并且不会被释放?
  2. 除了堆以外(已经优化并设置为64MB) ,还有其他有效降低整个Java进程内存消耗的方法吗?

看起来这对你很有用 https://dev59.com/AWw15IYBdhLWcg3wLIo2?rq=1 - codinnvrends
谢谢@codinnvrends,但我仍然不明白。我知道JVM除了堆之外还使用内存。我可以通过NMT完整地查看它,并在那里寻求优化。但我仍然不明白的是,即使NMT显示更少的内存消耗,在操作系统级别上也没有任何区别。 - KnotGillCup
2个回答

7

由于多种原因,NativeMemoryTracking 可能会报告比进程的实际常驻集大小(Resident Set Size,RSS)少的已提交内存。

  • NMT 仅计算某些 JVM 结构。它不计算内存映射文件(包括已加载的 .jar 文件),也不计算由其他库(即libjvm之外的库)分配的内存。即使是标准类库(即 libjava)分配的本地内存也不会在 NMT 报告中显示。

  • 当某些内容使用标准系统分配器(malloc)分配内存,然后将其释放时,这些内存并不总是返回到操作系统。系统分配器可能会将已释放的内存保留在池中以供将来重用,但从操作系统的角度来看,此内存被认为是已使用(因此包含在 RSS 中)。

这个答案这个视频 可以给出关于还需要哪些内存和如何分析 Java 进程占用空间的想法。

这篇文章 描述了一些关于减少占用空间的理智和极端想法。


感谢 @apangin 提供的详细答案和链接。我之前已经读过 Aleksey Shipilёv 的文章,它很棒。我知道 NMT 报告的已提交内存比进程实际的 RSS 少。但我仍然期望能看到一些改进。我猜即使 NMT 显示比没有 JVM 调整时少了 50MB,更多的内存也被分配了,并且现在没有返回给操作系统。 - KnotGillCup
1
@KnotGillCup 另一个可能的原因是,NMT显示的所有“已提交”内存并非都会增加进程的占用空间。例如,当您减小Java堆栈大小时,NMT将报告相应的已提交内存减少,但这不会影响RSS。有关详细信息,请参见JDK-8191369 - apangin
那肯定是这样的。我所做的事情之一就是减少堆栈大小。顺便说一句,视频很棒。我真的很喜欢它。 - KnotGillCup

1
首先,C程序的典型内存表示由以下部分组成。(https://www.geeksforgeeks.org/memory-layout-of-c-program/)
  A typical memory representation of C program consists of following sections.

  1. Text segment
  2. Initialized data segment
  3. Uninitialized data segment
  4. Stack
  5. Heap

JVM内存包括:JVM堆、JVM栈、JVM本地栈、程序计数器等。

(https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5)

并且 JVM 堆只是 Java 堆内存部分的一部分(在 C 程序中的内存表示中)。


谢谢,@SvenAugustus,但这并没有回答我的问题。我已经减少了JVM堆栈大小、GC内存分配等,但整个进程的内存与使用默认的JVM选项运行时一样。 - KnotGillCup

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