2

我有一个对象 (Object1) 的实例,其构造函数创建其他对象 (Object2) 的实例并将它们存储在 ArrayList 中。Object2 有一个静态 instancecount 变量(在其他静态变量中),并且 Object2 的实例依赖于这个 instancecount。在一个测试程序中,我运行了一个 for 循环,其中在每次迭代时都会创建一个 Object1 的实例,然后对 Object1 的实例的引用重新分配一个空值。我的测试程序看起来像:

    for (...) {
        Object1 obj = new Object1(...); //which creates several Object2's
        obj.myMethod();
        obj = null;
    }

问题是 Object2 的实例在每次循环迭代结束时没有得到垃圾收集,因此 Object2 的静态 instancecount 变量只会随着每次迭代而增长(随着内存使用量)。这是可以预料的吗?有没有办法强制清理 Object2 实例,以便每个循环都有一个新的开始?这是一个糟糕的程序设计吗?

谢谢!瑞安

4

4 回答 4

5

垃圾收集工作非常可靠,因此它不会泄漏内存或无限增长。

但是 GC 也仅在 JVM 认为需要时运行,因此您不能强制它在循环的每次迭代结束时运行。

拥有依赖于 GC 运行时间的代码是糟糕的代码。

另外,我不确定您如何管理实例计数。在构造函数中增加它很容易,但是什么时候再减少它呢?它肯定不会自动发生。

于 2012-11-12T02:14:50.797 回答
0

问题是 Object2 的实例在每次循环迭代结束时都没有得到垃圾收集,...。这是可以预料的吗?

是的。JVM 的默认行为是在最有效的时候运行垃圾收集器。这通常意味着当“新”空间填满时。GC 肯定不是“急切”的,它不会在对象变得不可访问时尝试回收对象。

有没有办法强制清理 Object2 实例,以便每个循环都有一个新的开始?

您可以调用System.gc()作为提示立即运行垃圾收集器。但是可以将 JVM 配置为忽略该提示。

这是一个糟糕的程序设计吗?

调用它通常是糟糕的设计System.gc()。运行 GC 相对昂贵,并且在不必要的时候(从人体工程学的角度)频繁运行它是非常浪费的。

编写依赖于立即运行的 GC 的代码总是不好的设计。事实上,任何需要使用习俗的东西都finalize()应该受到极大的怀疑。


我认为你真正的问题是:

.... 所以 Object2 的静态 instancecount 变量只会随着每次迭代而增长(随着内存使用)

这在一定程度上是正确的。但最终,JVM 会决定现在是运行 GC 的好时机,并且所有对象都将被回收。这就是 GC 的工作方式。

您真正的问题是(我认为)您对需要大量内存来保存所有尚未回收的垃圾对象这一事实感到不舒服。但另一方面,如果 GC ……和您的应用程序……有足够的内存可供使用,它会运行得更快。令人惊讶的是,分配和回收都比 GC 使用(例如)引用计数来确保尽快回收对象要快得多。


要注意的另一件事是,您必须使用终结器来递减实例计数器。这有一个不幸的副作用,即显着增加 GC 开销,并减慢对象的最终回收速度。

(发生的情况是,主 GC 注意到该对象是可终结的,并将其添加到队列中以供稍后终结。可终结的对象和任何可到达的对象都从可以立即回收的对象列表中删除。稍后点,终结线程遍历队列,调用finalize每个方法。如果这导致对象变得完全不可达,它们将在下一次 GC 运行时被回收。)

于 2012-11-12T03:11:51.653 回答
0

static instancecount variable (among other static variables)

查看 Object2 的静态变量。如果它们无论如何都引用了一个 Object2 实例,那么除非手动删除,否则该实例将永远不会被垃圾收集。

an object (Object1) whose constructor creates instances of other objects (Object2) and stores them in an ArrayList

这个 ArrayList 存储在哪里以及何时清除?尝试在 for 循环中从此列表中删除 Object2。

如果您可以显示更多代码,则调试起来会更容易。

于 2012-11-12T02:21:00.717 回答
0

感谢大家的回复!我重写了代码以不依赖于 GC。不幸的是,这意味着在每次循环运行时手动清除几个静态变量。这似乎有些尴尬,所以我想将来我应该小心运行工厂对象/方法的代码,其中包含循环中的静态变量。

以下是我从你们那里收到的一些问题的答案。再次,非常感谢!

Thilo:我通过构造函数增加实例计数,但不减少实例计数。这似乎适合解构器,尽管这些不存在,对吧。什么是减少它的适当方法?

Adrian:我尝试清除循环中的 ArrayList,但没有成功。ArrayList 是 Object1 的一个字段。

Stephen:您似乎刚刚回答了我上面关于如何减少实例计数的问题,但正如您所暗示的,这会增加不必要的开销。

Chamini2:我尝试在发布之前在 for 循环中调用 System.gc(),但它不想运行。显然这种方法只是一个建议。

于 2012-11-13T02:45:23.913 回答