1

我正在测试在 JDK 1.6 中运行的 java 应用程序中堆大小的使用。我使用工具 VisualVM 来监控堆使用情况。我在几分钟内发现最大堆大小使用量约为 500 MB。我使用了调用 System.gc() 的选项“Perform GC”。我第一次使用它时,最大堆减少到 410MB,然后我再次使用它来获得 130MB 和下一次到 85MB。我连续打了四个电话,没有任何间隔。为什么调用 System.gc() 没有在第一次将所有 Heap 收集到 85MB。这背后是否还有其他原因。或者我应该尝试任何其他方法?

4

3 回答 3

2

System.gc() 将在所有对象被扫描一次后返回。

一个对象应该在它被收集后被 finalized()。大多数对象不实现此方法,但对于那些实现的对象,它们被添加到队列中以便稍后清理。这意味着这些对象还不能被清理(不是保存它们的队列节点),即触发 GC 的行为会暂时增加内存消耗。

此外,还有一些对象的 SoftReferences 可能会或可能不会被 GC 清理。假设只有在没有清理太多其他内容的情况下才应清理这些内容。

简而言之,并不是所有的物体都可以在一个周期内清理干净。

于 2013-05-17T07:15:23.260 回答
0

System.gc() 请求 JVM 启动垃圾收集。如果您期望在 System.gc() 时立即调用 GC,那么这是一个错误的概念。多次调用它无济于事。无法将 System.gc() 映射到实际的垃圾收集。此外,无论您调用 System.gc() 多少次,JVM 只会在准备好执行 GC 时执行。可能发生的情况是,即使使用第一个 System.gc(),堆大小也会减少,但并不是在您调用它时就立即减少。与您的第一个 System.gc() 相关的垃圾收集可能正在后台完成,同时您的代码正在到达第三个 System.gc() 语句。

如果您非常确定仅添加多个 System.gc() 可以帮助您减少堆大小。然后您需要检查在第一个和最后一个 System.gc() 之间在 JVM 中创建的所有对象。可能有其他线程创建对象。

于 2013-05-17T07:16:20.100 回答
0

一个可能的原因可能是java.lang.ref.Reference类型的使用。如果 GC 要破坏“引用”,这将在 GC 本身完成后发生。任何因此变得无法访问的对象都留给下一个 GC 周期处理。

最终确定的工作方式相同。如果一个对象需要终结,它和所有可从它(仅)到达的对象很可能只能在下一个 GC 周期中收集。

还有一个问题是 GC 的堆收缩算法是非侵略性的。根据Java HotSpot VM 选项页面,如果垃圾回收后 70% 以上是空闲的,GC 只会缩小堆。但是,这是否指的是完整的 GC 尚不完全清楚。因此,您可以让 GC 进行部分 GC 并缩小,然后进行完整 GC 并进一步缩小。

(有些人从System.gc()javadocs 的措辞中推断出它将执行完整的 GC。但是,我怀疑这实际上是版本/ GC 相关的。)


但老实说,这一切都应该没有实际意义。试图强迫应用程序尽可能多地返还内存是没有意义的。您可能会强迫它丢弃缓存的数据。当应用程序再次激活时,它将开始重新加载其缓存。

于 2013-05-17T07:18:09.510 回答