堆转储不等于虚拟内存?

3

虽然我对Java和特别是Java调试不是很了解,但使用Monitoring在Jenkins中获取堆转储,并在Eclipse中使用MAT进行解码后,显示总内存使用量为169.4 MB,而在Jenkins监视器中,内存似乎一直被大量使用,并且频繁运行GC。-XmX为4G。

为什么我只能用MAT得到169.4 MB?这可能是因为在进行转储之前,Jenkins执行了GC吗?如果是这样,我能否避免它以查看完整的内存转储?


在这种情况下,插件可能会停止当前的任务或等待其完成(或者当前任务加载了大量数据但不再需要)。我只是猜测,因为我不知道那个插件。但除此之外,您是否遇到频繁GC的问题? - Thomas
@Thomas 我不这么认为。使用4G内存,GC大约每30秒执行一次,只需要1秒钟的时间。我遇到的问题是页面/控制台日志加载缓慢,但没有网络问题,并且Jenkins日志中也没有任何信息。没有任何错误,只是页面加载缓慢。即使在主节点上运行64位RHEL并且有4个核心和24G RAM以及其他6个使用较少内存的Jenkins实例,系统负载也很少超过1。 - Zloj
通常情况下,当内存被无法进行垃圾回收的对象填满时,您需要进行转储以解决内存问题。因此,您通常不希望看到可以被回收的对象。 - Thomas
报告中关于“已使用的非堆内存”有何说明?(现在有两个Thomas) - Thomas Weller
@Thomas,我在MAT的任何报告中都看不到这个。我在哪里可以查看“已使用的非堆内存”?该文件具有.hprof扩展名,我认为应该表示它仅限于堆。我还没有进行过未经GC的转储。 - Zloj
显示剩余7条评论
3个回答

3

了解内存

是的,Java堆转储和虚拟内存转储(在Windows上称为“崩溃转储”或“内存转储”)是不同的东西。

Java堆转储仅包含与Java相关的内存,即Java对象所在的位置。可以使用MAT(如您所提到的)或Java堆分析工具等工具来分析Java堆转储。

(用户模式)进程的Windows崩溃转储包含所有虚拟内存,其中虚拟内存是操作系统提供内存的术语。在Windows上,这是通过VirtualAlloc分配的所有内存。

操作系统虚拟内存将包括Java堆,因为Java只能从操作系统请求内存。

因此,在比较内存大小时,重要的是要了解工具是针对Java还是操作系统的。

在您的情况下,监控看起来很像一个通用工具,因为它处理进程列表和CPU时间,似乎没有Java特定的内容。另一方面,从其描述中可以清楚地看出,MAT是一个Java工具。

内存差异

那么Java堆大小与虚拟内存大小相差多少呢?

很多:

  1. 加载的EXE / DLL占用虚拟内存,但不占用Java堆
  2. 本地代码使用的内存(例如通过JNI)占用虚拟内存,但不占用Java堆
  3. Java从操作系统请求但尚未使用的内存绝对占用虚拟内存,但可能会从Java角度报告为“空闲”。

谢谢你的好答案。只有一个小修正:在这种情况下,monitoring实际上是监视Jenkins JVM而不是操作系统。 - Zloj

1
显然,收集堆转储的工具会执行GC以减少转储的大小。由于可以进行GC的内容不应该导致OOM,这旨在查找内存泄漏而非解决内存使用问题。

0

虚拟机请求虚拟内存来存储各种数据。然后,虚拟机分配一些内存来存储变量(即堆),本地代码(非堆),保留一些尚未使用的内存。因此,虚拟内存严格大于堆。由于您可以编写简单的无限递归(堆将几乎与虚拟内存一样大)或在 hello world 程序中加载大型 dll(堆比虚拟内存小得多),因此堆和虚拟内存之间没有明显的相关性。


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