我正在测试在JDK 1.6中运行的Java应用程序中Heap大小的使用情况。我使用工具VisualVM来监视堆使用情况。我发现几分钟内最大堆大小使用量约为500 MB。我使用选项“Perform GC”调用System.gc()。我第一次使用它时,最大堆减少到410MB,然后再次使用它获得130MB,下一次使用85MB。我没有任何间隔就完成了接下来的所有四个电话。为什么调用System.gc()不会在第一时间将所有堆收集到85MB。这有什么其他原因吗?或者我应该尝试其他任何方法?
答案 0 :(得分:2)
当所有对象都被扫描一次时,System.gc()将返回。
一个对象应该在收集之后完成()。大多数对象不实现此方法,但对于那些方法,它们被添加到队列中以便稍后清理。这意味着这些对象尚未被清除(而不是保存它们的队列节点),即触发GC的行为可以暂时增加内存消耗。
此外,对于可能会或可能不会被GC清除的对象存在SoftReferences。假设只有在没有其他清理的情况下才能清理这些内容。
简而言之,并非所有对象都可以在一个周期内清理。
答案 1 :(得分:0)
System.gc()请求JVM启动垃圾回收。如果您希望在System.gc()之后立即调用GC,那么这是一个错误的概念。多次调用无济于事。无法将System.gc()映射到实际的垃圾收集。此外,无论您调用System.gc()多少次,JVM只会在准备好时才执行GC。可能会发生的事情是,即使使用第一个System.gc(),堆大小也会减少,但是一旦调用它就不会很快。与您的第一个System.gc()相关的垃圾收集可能在后台完成,并且您的代码并行到达第三个System.gc()语句。
如果您非常确定只添加多个System.gc()可以帮助您减小堆大小。然后,您需要检查在第一个和最后一个System.gc()之间在JVM中创建的所有对象。可能有其他线程创建对象。
答案 2 :(得分:0)
一个可能的原因可能是使用java.lang.ref.Reference
类型。如果GC打算打破"参考"这将在GC本身完成后发生。因此,任何无法访问的对象都将留待下一个GC循环处理。
终结工作方式相同。如果一个对象需要最终确定,那么它和从中可以访问的所有对象(仅)可能只能在下一个GC循环中收集。
然后存在GC的缩小堆算法非侵略性的问题。根据{{3}}页面,如果垃圾收集后有超过70%的空闲时间,GC只会收缩堆。但是,如果这是指完整的GC,则不完全清楚。因此,您可以让GC进行部分GC并缩小,然后使用完整的GC并缩小一些GC。
(有些人从System.gc()
javadocs的措辞推断出它将执行完整的GC。但是,我怀疑这实际上是版本/ GC依赖。)
但说实话,这应该都没有实际意义。试图强迫应用程序尽可能多地回放内存是没有意义的。很可能是你强迫它丢弃缓存的数据。当应用程序再次处于活动状态时,它将开始重新加载其缓存。