3

我正在使用 NetBeans IDE,它给了我一个对我来说没有意义的警告。警告指出“在构造函数中泄漏这个”。以下代码是基本设置(我只是删除了与问题无关的代码)。基本上我只想保留所有Square制作对象的列表。这是我需要担心的警告吗?或者它只是根据情况而导致内存泄漏的可能原因?

无论哪种方式,有人可以解释为什么这会被视为泄漏吗?

public class Square {
    private static ArrayList<Square> squares;

    public Square() {
        if(squares == null) {
            squares = new ArrayList<>();
        }

        squares.add(this); // I get a warning on this line
    }
}

我知道这只是一个警告,但我不喜欢忽略警告,除非我完全了解正在发生的事情并且可以针对特定情况做出明智的选择。

谢谢!

4

7 回答 7

4

(不是真正的答案,但是...)

如果您的目标确实是在列表中维护您创建的所有方块的列表,那么有一种更好的方法可以实现:

public class Square
{
    private static final List<Square> allSquares = new ArrayList<Square>();

    // Constructor: private!
    private Square() {}

    // Create a square
    public static Square newSquare()
    {
        Square ret = new Square();
        allSquares.add(ret);
        return ret;
    }
}

您会注意到构造函数中没有this转义。

要创建一个新正方形,您将执行以下操作:

Square mySquare = Square.newSquare();
于 2013-01-09T06:09:59.360 回答
3

我认为警告与垃圾收集无关,尽管这确实是一个问题。实例永远不会被 GC 处理(除非ClassLoader收集了整个实例)。

警告是说this正在从构造函数中传递给另一个方法。在构造函数完成之前,根据构造函数this所包含的逻辑,不一定是一个完整的和初始化的对象。构造函数中的任何事情都旨在在其他任何事情接触到对象之前发生。this但是在构造函数完成之前还有其他东西要使用。这可能会导致令人惊讶的错误。

于 2013-01-09T06:03:52.150 回答
2

有人可以解释为什么这会被认为是泄漏吗?

这是一个(潜在的)泄漏,因为squares列表和列表中的任何对象都不会被垃圾收集。如果没有其他代码可以从列表中删除对象,或者清除列表或为空列表,则对象将通过列表泄漏。


也许您需要了解在垃圾收集语言的上下文中“内存泄漏”是什么意思。在像 C 或 C++ 这样的语言中,当对象丢失时会发生存储泄漏;即应该释放/处置对象的代码未能这样做。在垃圾收集语言中,当 GC 未能释放/处置对象时会发生泄漏,因为它似乎仍在使用中;即GC仍然可以通过跟踪找到对象。


然而,在重读这个问题时,我同意肖恩欧文的观点。消息“Leaking this in constructor”很可能是在谈论构造函数在构造函数完成之前使对象的引用可见的事实。这也称为“不安全发布”。它可能是潜在的并发错误的来源。(这甚至可能是单线程应用程序中的问题;例如,如果您创建 Square 的子类 ...)

于 2013-01-09T06:01:41.203 回答
1

免责声明:这只是一个猜测!

如果这是唯一squares被修改的地方,那么这意味着Square对象永远不会被垃圾收集,因为每个对象总是至少有一个引用。如果是这样,也许您的 IDE 足够聪明,可以发现这一点。

于 2013-01-09T06:00:50.880 回答
1

为什么在构造函数中添加静态 ArrayList 会导致内存泄漏?

没有。这不是您收到警告的根本原因。然而,还有许多其他问题。

  1. this不应在构造函数之外传递。(如果您不遵守此规定,可能会出现零星问题)

  2. Square类包含所有它创建的对象的列表。这意味着对于创建的每个对象,至少存在一个引用。

    Square aSq = new Square(); // two references, aSq and reference in ArrayList
    new Square(); // one reference in ArrayList
    

    因此,直到类存在于内存中,所有创建的对象都不会被垃圾回收。因此内存泄漏。

于 2013-01-09T06:12:18.887 回答
0

我假设您了解静态变量由 ClassLoaders 引用的 Class 对象引用。因此,除非您的 ClassLoader 本身有资格进行垃圾收集,或者 ClassLoader 通过某种方式丢弃您的引用类,否则它永远不会被垃圾收集。因此,每次调用构造函数时,都会将数据添加到几乎永远不会被垃圾收集的列表中。

于 2013-01-09T06:04:11.293 回答
0

无论出于何种原因,我认为这是一种不好的做法。

OOP 存在的理由是封装一组合约。并且这些合同必须对课程的订阅者清晰可见。

当有人实例化new Square()时,对于程序员来说,实例化也将此实例添加到非垃圾收集列表中并不明显。

所以,我程序员很乐意频繁地实例化 Square,并且相信只要我有意识地取消对每个平凡实例化 Squares 的所有引用,它就会被垃圾收集。但我错了——因为尽管我做出了所有有意识的努力,但没有一个实例会被垃圾收集。

你知道,垃圾收集为我们程序员提供了一些习惯性的捷径。例如,下面的 while 结构例示了简单实例化对象的简化。程序员对反复重新实例化 String ins 没有疑虑,因为他/她知道前一个实例将被垃圾收集。

String ins = new String();
while(c != null && ins != null) {
  ins = new String();
  readInputInto(ins);
}

但是,用您的 Square 替换 String 并将其交到普通程序员的手中。程序员会假设与 String 相同的垃圾回收能力!

因此,尝试在构造函数中做太多事情并不是一个好习惯,尤其是在构造函数中隐藏一个具有重大影响的动作。

您应该简单地将实例添加到构造函数之外的列表中。

于 2013-01-09T06:30:03.143 回答