【问题标题】:Invoking System.gc() from YourKit causes allocated memory to increase从 YourKit 调用 System.gc() 会导致分配的内存增加
【发布时间】:2018-09-18 14:49:33
【问题描述】:

我即将开始使用第 3 方闭源库来加载一堆数据,并想检查它的速度以及加载一个“集”数据需要多少内存。我编写了这个简单的工具来调用数据加载来计时,并使用 YourKit 快速查看内存使用情况并深入研究 CPU 时间。

我在 Windows 7 上运行它,在 JDK8 上使用 Eclipse,没有 VM 参数。

public static void main(String[] args){
    long start = System.currentTimeMillis();

    // There are a few more calls involved, but not much
    BlackBoxDataProvider bd = new BlackBoxDataProvider("c:\\thedata");
    BlackBoxData = bd.loadTheData();
    System.out.println(System.currentTimeMillis() - start + "ms");

    // Keep the application alive so I can have a quick look at memory usage
    while(true) {
        Thread.sleep(1000);
    }
}

这是加载完成后 YourKit 的内存快照:

然后我使用 YourKit 来“强制”垃圾收集,这发生了:

显然这不是现实生活中的场景,因为我被困在 main 方法中,在主线程上,所以我的一些引用不会被清理,但我不知道为什么内存分配会保持增加。

每次我单击“强制系统 GC”时,分配都会增加。在停止增加之前,我达到了 11.9GB。

为什么会这样?

【问题讨论】:

  • 据我所见,堆的行为符合预期,即 Eden 空间和 Old Gen 不参与该增长。只有“分配的所有池”在增长……
  • 是的,我的问题是为什么“分配的所有池”不断增长。
  • 这归结为“分配的所有池”实际上意味着什么的问题,只有 YourKit 可以回答。是 MetaSpace、堆外内存(I/O 缓冲区)还是其他?那么,您是否尝试过使用不同的监控工具(例如 VisualVM 或 FlightRecorder)并比较结果?
  • 使用 ProcessExplorer 显示私有字节的增长速度与“分配所有池”的速度相同。该进程继续分配更多内存,大概是为 GC 分配的,我想知道它为什么分配这么多
  • 我并不怀疑它在分配字节,但了解 为什么 或用于什么目的会很有帮助。如前所述,此内存不属于普通堆,因此了解实际类别会有所帮助。其他工具,如提到的 VisualVM 或 FlightRecorder 可能会提供更有用的统计信息。此外,尝试使用其他工具有助于验证内存分配不是由 YourKit 本身引起的。您也可以将while(true) { Thread.sleep(1000); }替换为while(true) { Thread.sleep(1000); System.gc(); },看看应用触发的GC是否有类似的行为。

标签: java garbage-collection yourkit


【解决方案1】:

当所有对象都被扫描一次后,System.gc() 将返回。如果将实现 finalize() 方法的对象添加到队列中,以便稍后清理。这意味着这些对象还不能被清理(不是保存它们的队列节点),即触发 GC 的行为可能会暂时增加内存消耗。这就是您的情况可能发生的情况。

总之,并不是所有的对象都可以在一个周期内清理干净。

【讨论】:

  • 如果堆本身被填满并且需要 Full GC 怎么办。 GC线程在这种情况下使用什么空间,保留空间?
  • 第一次调用是有道理的,但它是否会不断复制引用队列以进行清理?使用的内存没有增加,只是 JVM 正在抓取的分配。
  • @AvinashReddyPaduri 如果堆完全满了,你会得到一个 OutOfMemoryError
  • @beirtipol 它添加了一个终结队列。您调用 gc 的次数越多,添加到队列中的对象就越多。我建议阅读Java Hotspot Garbage collection
  • GC 不仅会将需要终结的对象排入队列,还会将reference objects 用于弱引用、软引用和幻像引用。在 HotSpot/OpenJDK 上,具有非平凡 finalize() 方法的对象将获得一个在 API 中不可见的 special reference 对象,该对象在创建对象时已经创建。因此,GC 仅使用其中的字段链接这些对象,因此不需要分配。
猜你喜欢
  • 1970-01-01
  • 2018-08-20
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 2011-08-17
  • 2019-08-12
  • 2018-04-21
  • 1970-01-01
相关资源
最近更新 更多