在Java中将对象分配为null会影响垃圾回收吗?

时间:2009-01-16 03:30:03

标签: java null garbage-collection

在Java中为null分配未使用的对象引用是否以任何可衡量的方式改进了垃圾收集过程?

我使用Java(和C#)的经验告诉我,尝试和智能虚拟机或JIT编译器通常是反直觉的,但我看到同事使用这种方法,我很好奇这是不是很好练习拾取或其中一个伏都教编程迷信?

14 个答案:

答案 0 :(得分:61)

通常,没有。

但是喜欢所有的事情:这取决于。如今,Java中的GC非常好,所有内容都应该在不再可用后立即清理。这就是在为局部变量留下一个方法之后,以及当不再为字段引用类实例时。

如果你知道它会继续引用,你只需要显式为null。例如,保持在周围的阵列。您可能希望在不再需要数组的各个元素时将其置零。

例如,这个代码来自ArrayList:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
         System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
    elementData[--size] = null; // Let gc do its work

    return oldValue;
}

此外,只要没有引用仍然存在,显式地使对象无效就不会导致对象被收集得比它刚刚自然超出范围。

这两种:

void foo() {
   Object o = new Object();
   /// do stuff with o
}

void foo() {
   Object o = new Object();
   /// do stuff with o
   o = null;
}

功能相同。

答案 1 :(得分:11)

根据我的经验,人们往往忽略了偏执狂的引用而非必要。这是一个快速指南:

  1. 如果对象A引用对象B ,则不再需要此引用对象A不符合垃圾回收条件,那么您应该明确地将该字段置空。如果封闭对象无论如何都要收集垃圾,则无需将字段置空。在dispose()方法中删除字段几乎总是没用。

  2. 无需将方法中创建的对象引用置空。一旦方法终止,它们将自动清除。此规则的例外情况是,如果您运行的是非常长的方法或某个大型循环,则需要确保在方法结束之前清除某些引用。同样,这些案件极为罕见。

  3. 我想说绝大多数时候你不需要废除引用。试图超越垃圾收集器是没用的。你最终会得到效率低下,难以理解的代码。

答案 2 :(得分:10)

好文章今天是coding horror

GC的工作方式是查找没有任何指针的对象,搜索区域是堆/堆栈以及它们拥有的任何其他空间。因此,如果将变量设置为null,则实际对象现在不会被任何人指向,因此可能是GC。

但是由于GC可能不会在那个确切的瞬间运行,你可能实际上并没有自己买任何东西。但是如果你的方法相当长(就执行时间而言)可能是值得的,因为你将增加GC收集该对象的机会。

问题也可能因代码优化而变得复杂,如果在将变量设置为null后从不使用该变量,则删除将值设置为null(少执行一条指令)的行将是一种安全的优化。所以你可能实际上没有得到任何改进。

总而言之,是的,它可以提供帮助,但不会是确定性的

答案 3 :(得分:8)

至少在java中,它根本不是巫毒编程。使用类似

之类的东西在java中创建对象时
Foo bar = new Foo();

你要做两件事:第一,你创建一个对象的引用,第二,你创建Foo对象本身。只要该引用或其他引用存在,特定对象就不能被gc'd。但是,当您为该引用分配null时...

bar = null ;

并假设没有其他任何东西对该对象有引用,它在下次垃圾收集器经过时被释放并可用于gc。

答案 4 :(得分:7)

取决于。

一般来说,保持对对象的引用越短,收集的速度越快。

如果您的方法需要2秒钟才能执行,并且在执行一秒钟后不再需要对象,则清除对它的任何引用都是有意义的。如果GC在一秒钟后看到,您的对象仍然被引用,下次它可能会在一分钟左右检查它。

无论如何,默认情况下将所有引用设置为null对我来说是过早优化,没有人应该这样做,除非在极少数情况下它可以显着减少内存消耗。

答案 5 :(得分:6)

显式设置null引用而不是让变量超出范围,对垃圾收集器没有帮助,除非保持的对象非常大,一旦你完成就把它设置为null是一个很好的想法。

通常设置对null的引用,意味着该对象完全使用的代码的READER,不应再关注。

通过添加一组额外的大括号来引入更窄的范围,可以实现类似的效果

{
  int l;
  {  // <- here
    String bigThing = ....;
    l = bigThing.length();
  }  // <- and here
}

这允许bigThing在离开嵌套大括号后立即进行垃圾收集。

答案 6 :(得分:5)

public class JavaMemory {
    private final int dataSize = (int) (Runtime.getRuntime().maxMemory() * 0.6);

    public void f() {
        {
            byte[] data = new byte[dataSize];
            //data = null;
        }

        byte[] data2 = new byte[dataSize];
    }

    public static void main(String[] args) {

        JavaMemory jmp = new JavaMemory();
        jmp.f();

    }

}

以上程序抛出OutOfMemoryError。如果您取消注释data = null;,则OutOfMemoryError已解决。将未使用的变量设置为null

始终是一种好习惯

答案 7 :(得分:4)

我曾经在视频会议应用程序上工作过一次,并且当我不再需要该对象时,我花时间将引用归零时,发现性能上存在巨大的巨大差异。这是在2003-2004,我只能想象GC从那时起变得更加聪明。在我的情况下,我每秒都有数百个物体进出范围,所以我注意到GC定期开始运行。然而,在我指出null对象后,GC停止了暂停我的应用程序。

所以这取决于你在做什么...

答案 8 :(得分:2)

是。

来自“务实的程序员”第292页:

通过将引用设置为NULL,可以将对象的指针数减少一个...(这将允许垃圾收集器将其删除)

答案 9 :(得分:1)

我认为OP指的是这样的事情:

private void Blah()
{
    MyObj a;
    MyObj b;

    try {
        a = new MyObj();
        b = new MyObj;

        // do real work
    } finally {
        a = null;
        b = null;
    }
}

在这种情况下,VM一旦离开范围就不会为GC标记它们吗?

或者,从另一个角度来看,明确地将项目设置为null会导致他们在他们刚刚超出范围之前获得GC吗?如果是这样,当不需要内存时,VM可能花费时间对对象进行GC操作,这实际上会导致CPU使用率下降,因为它会更早地使用GC。

答案 10 :(得分:0)

取决于

我不知道Java,但在.net(C#,VB.net ...)中,当您不再需要对象时,通常不需要指定null。

但请注意,“通常不需要”。

通过分析您的代码,.net编译器可以很好地估计变量的生命周期......以准确地判断对象何时不再使用。因此,如果你编写obj = null,它实际上看起来好像仍然在使用obj ...在这种情况下,分配null是有效的。

在某些情况下,实际上可能有助于分配null。一个例子是你有一个很长时间运行的庞大代码或一个在不同线程或某个循环中运行的方法。在这种情况下,可能有助于分配null,以便GC很容易知道它不再被使用。

没有硬&amp;快速的规则。按上述位置进行null分配代码并运行分析器以查看它是否有任何帮助。很可能你可能没有看到任何好处。

如果是.net代码,你正在尝试优化,那么我的经验是,使用Dispose和Finalize方法实际上比打扰空值更有益。

关于该主题的一些参考文献:

http://blogs.msdn.com/csharpfaq/archive/2004/03/26/97229.aspx

http://weblogs.asp.net/pwilson/archive/2004/02/20/77422.aspx

答案 11 :(得分:0)

即使使参考文献无效也略微提高效率,是否值得为这些丑陋的无效化而烦恼?它们只会混乱并且模糊包含它们的意图代码。

它是一个罕见的代码库,没有比尝试超越垃圾收集器更好的优化候选者(更少见的是开发人员,他们成功地超越了它)。你的努力很可能会更好地花在其他地方,放弃那个狡猾的Xml解析器或找到一些缓存计算的机会。这些优化将更容易量化,并且不要求您使用噪声污染代码库。

答案 12 :(得分:0)

在将来执行程序时,某些数据成员的值将用于计算程序外部可见的输出。根据未来(并且无法预测)对计划的输入,可能会或可能不会使用其他人。可能保证不使用其他数据成员。分配给这些未使用数据的所有资源(包括内存)都被浪费了。垃圾收集器(GC)的工作是消除浪费的内存。 GC消除所需的东西将是灾难性的,因此使用的算法可能是保守的,保留超过严格的最小值。它可能会使用启发式优化来提高其速度,但代价是保留一些实际上不需要的项目。 GC可能会使用许多潜在的算法。因此,您对程序所做的更改以及不影响程序正确性的更改可能会影响GC的运行,或者使其运行速度更快以执行相同的工作,或者更快地识别未使用的项目。所以这种改变,在理论上设置一个不熟悉的对象引用null并不总是伏都教。

是伏都教吗?据报道,部分Java库代码可以执行此操作。该代码的编写者比普通程序员好得多,他们知道或者与了解垃圾收集器实现细节的程序员合作。所以这表明 有时是一种好处。

答案 13 :(得分:0)

正如您所说的那样,存在优化,即JVM知道上次使用该变量的位置,并且可以在最后一点之后立即对它引用的对象进行GC(仍在当前作用域中执行)。因此,在大多数情况下取消引用对GC毫无帮助。

但是避免“裙带关系”(或“浮动垃圾”)问题(read more herewatch video)可能很有用。存在此问题是因为堆分为老一代和年轻一代,并且应用了不同的GC机制:次要GC(快速且经常发生在清理年轻一代)和主要Gc(导致清理老一代的暂停时间更长)。如果“裙带关系”被已经存在于老一代中的垃圾所引用,则不允许收集年轻一代中的垃圾。

这是“病理性的”,因为任何提升的节点都将导致所有后续节点的提升,直到GC解决问题为止。

为了避免裙带关系,从应该被删除的对象中删除引用是一个好主意。您可以看到此技术已应用于JDK类:LinkedListLinkedHashMap

private E unlinkFirst(Node<E> f) {
    final E element = f.item;
    final Node<E> next = f.next;
    f.item = null;
    f.next = null; // help GC
    // ...
}