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


85

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

我在Java(和C#)方面的经验告诉我,尝试超越虚拟机或JIT编译器通常是反直观的,但是我看到同事使用这种方法,我很好奇这是否是一个好的选择或其中一种巫术编程迷信?

Answers:


66

通常没有。

但是像所有事物一样:这取决于。如今,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;
}

在功能上是等效的。


7
您可能还想在可能使用大量内存或执行很长时间的方法内部,或者在可能运行很长时间的Swing应用程序中的代码中使引用无效。在简短的方法或短暂的对象中,不值得使用额外的令人困惑的代码。
约翰·加德纳

1
是的,当我们将一个对象设为空时,垃圾收集器会立即收集该对象?
穆罕默德·巴巴尔

1
不会。垃圾回收器会定期执行,以查看是否存在没有引用变量指向的对象。当将它们设置为null然后调用时System.gc(),它将被删除(前提是没有其他指向该对象的引用)。
JavaTechnical

2
@JavaTechnical据我所知,不能保证通过调用System.gc()来启动垃圾回收。
2015年

12

以我的经验,人们常常出于偏执狂而不是出于必要而取消引用。这是一个快速指南:

  1. 如果对象A引用了对象B,并且您不再需要该引用,并且对象A不符合垃圾回收的条件,则应显式将字段清空。如果封闭的对象无论如何都要收集垃圾,则无需清空字段。在dispose()方法中淘汰字段几乎总是无用的。

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

我要说的是,在绝大多数情况下,您将不需要删除引用。试图超越垃圾收集器是没有用的。您将最终得到效率低下,无法读取的代码。


11

好文章是当今的编码恐怖

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

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

代码优化也会使问题变得复杂,如果您在将变量设置为null之后再也不使用变量,那么删除将值设置为null的行(减少执行一条指令)将是一种安全的优化方法。因此,您实际上可能没有任何改善。

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



安全优化的要点是删除将ref设置为null的行。
1998年

8

至少在Java中,这根本不是伏都教编程。当您使用类似的方式在Java中创建对象时

Foo bar = new Foo();

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

bar = null ;

并假设没有其他对象对该对象的引用,则该对象将在下一次垃圾收集器通过时释放并可供gc使用。


12
但是超出范围将执行相同的操作而无需额外的代码行。如果它是方法本地的,则没有必要。离开方法后,该对象将有资格使用GC。
duffymo,2009年

2
好。现在,对于一个流行测验,构造一个示例代码,对于该示例而言,这还不够。提示:它们存在。
查理·马丁

7

这取决于。

一般而言,您保留对对象的引用的时间越短,收集它们的速度就越快。

如果您的方法要花2秒执行时间,并且在方法执行一秒钟后不再需要对象,则清除对它的所有引用都是有意义的。如果GC在一秒钟后看到该对象,则仍在引用您的对象,下次它可能在一分钟左右的时间内进行检查。

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


6

明确地将引用设置为null而不是仅仅让变量超出范围不会对垃圾收集器有所帮助,除非所保存的对象非常大,这是个好主意,将对象设置为null即可。

通常将引用设置为null,表示此对象已完全完成且不再关注的代码的READER。

通过引入额外的括号来引入更窄的范围,可以达到类似的效果

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

这样就可以在离开嵌套括号之后立即对bigThing进行垃圾收集。


不错的选择。这样可以避免将变量显式设置为null以及避免在某些IDE中显示关于未使用该null分配的警告。
jk7

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


也就是说,imo是进入稻草人争论世界的坚实基础。仅仅因为您可以创建一种情况,将变量设置为null即可在特定情况下解决问题(并且我不认为您已这样做)并不意味着在每种情况下都将其设置为null是个好主意。添加代码以将您不再使用的每个变量设置为null,即使之后不久它们将超出范围,也只会增加代码膨胀,使代码的可维护性降低。
RHSeeger

为什么在变量数据超出范围时不收集byte []数组垃圾?
Grav

2
@Grav:超出范围并不能保证垃圾收集器将收集本地对象。但是,将其设置为null可以防止OutOfMemory异常,因为可以确保GC将在诉诸抛出OutOfMemory异常之前运行,并且如果将对象设置为null则它将可以收集。
Stefanos T.


4

我曾经在一个视频会议应用程序上工作,发现当我不再需要该对象时,便花时间对空引用进行了性能上的巨大巨大改变。那是在2003-2004年,我只能想象GC从那以后变得更加智能。以我为例,每秒有数百个对象进入和离开作用域,因此当GC定期启动时,我注意到了它。但是,在我将其指向空对象之后,GC停止了暂停我的应用程序。

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


2

是。

摘自“实用程序员” p.292:

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


我猜这仅适用于引用计数算法。是否在使用中?
Nrj

3
对于任何现代垃圾收集器来说,此建议都是过时的。引用计数GC有很多问题,例如循环引用。
劳伦斯·多尔

在标记扫描GC中也是如此。可能在其他所有方面。您拥有少一个参考的事实并不意味着它已被计算在内。通过调整的答案可以解释这一点。缩小范围比设置为NULL更好。

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处理。


GC运行的时间是不确定的。我不相信设置对象完全不会null影响GC行为。
harto

0

这取决于

我不了解Java,但是在.net(C#,VB.net ...)中,当您不再需要对象时,通常不需要分配null。

但是请注意,它“通常不是必需的”。

通过分析您的代码,.net编译器可以很好地评估变量的使用寿命...以准确判断何时不再使用该对象。因此,如果您编写obj = null,则实际上看起来好像仍在使用obj ...在这种情况下,分配空值会产生反作用。

在某些情况下,分配空值实际上可能会有所帮助。一个示例是您有一个长时间运行的巨大代码,或者一个方法在另一个线程或某个循环中运行。在这种情况下,分配空值可能会有所帮助,以便GC可以轻松知道不再使用它。

没有硬性规定。在代码的上面将null赋值,然后运行分析器以查看它是否有任何帮助。很可能您可能看不到好处。

如果您要优化的是.net代码,那么我的经验就是,谨慎对待Dispose和Finalize方法实际上比打扰null更有益。

关于该主题的一些参考:

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

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


0

即使取消引用的效率稍高一些,是否值得用这些丑陋的无效方法添加代码来使您感到丑陋呢?它们只会很混乱,并且会掩盖包含它们的意图代码。

它是一种罕见的代码库,没有比试图使Garbage收集器更聪明的优化候选者了(稀有的开发者还是成功地超过了Garbage Collector)。您的工作很可能会花在其他地方,而不是放弃笨拙的Xml解析器,或者找到一些缓存计算的机会。这些优化将更容易量化,并且不需要您将代码库弄得脏noise的。


0

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

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


0

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

但是,避免“裙带关系”(或“浮动垃圾”)问题可能很有用(请在此处阅读更多信息观看视频)。存在此问题是因为堆分为老一代和年轻一代,并且应用了不同的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
    // ...
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.