2010-05-28 90 views
47

帮助我解决与同事的争议: 是否将Java中的变量或集合设置为null帮助垃圾回收并减少内存使用量?如果我有一个长时间运行的程序,并且每个函数都可能被迭代地调用(可能数千次):在将值返回给父函数之前,是否将其中的所有变量设置为null有助于减少堆大小/内存使用情况?是否将variable = null设置为垃圾回收

+4

+1好问题。 – CoolBeans 2010-05-28 19:07:50

回答

57

这是旧的表现传说。这在1.0天内就是真的,但编译器和JVM已经得到改进以消除需求(如果有的话)。这个优秀的IBM文章进入细节,如果你有兴趣:Java theory and practice: Garbage collection and performance

+0

+1:这是一篇很棒的文章,只是我寻找的弹药。 – ashurexm 2010-05-28 17:52:33

+0

链接被破坏,请修复链接! – 2013-10-27 10:55:27

+0

该文章仍可在http://web.archive.org上找到。请参阅: http://web.archive.org/web/20130928235110/http://www.ibm.com/developerworks/java/library/j-jtp01274/index.html – 2013-11-05 10:13:35

3

不一定。当没有活动线程持有对该对象的引用时,对象就有资格进行垃圾回收。

当方法返回时,局部变量超出范围,并且根本没有意义将局部变量设置为null - 变量消失,如果没有其他东西持有引用变量引用的对象,那么这些对象就有资格进行垃圾回收。

关键不在于查看变量,而是查看这些变量所引用的对象,并找出这些对象由程序引用的位置。

2

它是局部变量没用,但它可以是有用/需要清理不需要的了(例如后初始化)实例变量。

(是啊是啊,我知道如何应用Builder模式......)

+0

它**可以**在本地变量上有用,如果编译器不能自己解决变量将不会被再次读取。在大多数情况下,编译器**可以**自己完成工作。 – 2010-11-25 09:36:14

+0

@IanRingrose在退出程序之前,问题是关于null。这种做法是无用的。编译器不参与垃圾收集。 – EJP 2016-03-01 21:49:42

-4

这是一个好有。将对象设置为null时,可能会在即时GC循环中更快地垃圾收集对象。但是在给定的时间没有保证机制来收集对象垃圾。

+0

这可能是在某些特殊情况下是个好主意,但你当然不应该总是设置变量'null'(注意,它是精确很重要:你不能“设置对象为空”)出于习惯,不加思考。这导致了一种迷信的编程,你不知道你为什么做事,这是一个坏主意。 – Jesper 2010-05-28 17:49:26

+0

问题是关于在返回之前调零,这是浪费时间。 – EJP 2016-03-01 21:50:47

5

的Java虚拟机规格

12.6.1实施定稿 每个对象都可以由两个属性来表征的:它可以是可到达的,终结器可达的,或不可达,并且它也可以是未确定的,终结,或最终确定。

可达的对象是可以在任何可能继续计算任何活动线程访问的任何对象。优化程序的转换可以设计为将可达到的对象数量减少到小于天真被认为可达的对象数量。例如,编译器或代码生成器可以选择将不再用于空的变量或参数设置为使得此类对象的存储器可以更快地被回收。

讨论

如果在对象的字段中的值被存储在寄存器发生的另一个例子。程序然后可以访问寄存器而不是对象,并且再也不访问对象。这意味着该对象是垃圾。

如果对象可能涉及任何潜在的继续计算,则该对象是可到达的。所以如果你的代码引用了一个局部变量,而没有其他引用它,那么你可能会通过将它设置为null来收集对象。这可能会给出一个空指针异常,或者改变你的程序的行为,或者如果它没有,你不需要第一个变量。

如果您将字段或数组元素置空,那么对于某些应用程序来说这可能是有意义的,并且会导致内存更快地被回收。一旦大小写正在创建一个大型数组来替换由类中的字段引用的现有数组 - 如果在创建替换之前的空字段被创建,那么它可能会减轻对内存的压力。

Java的另一个有趣的特性是范围不会出现在类文件中,因此范围与可达性无关;这两种方法创建相同的字节码,因此虚拟机不会看到创建的对象的所有范围:

static void withBlock() { 
    int x = 1; 

    { 
     Object a = new Object(); 
    } 

    System.out.println(x+1); 
} 

static void withoutBlock() { 
    int x = 1; 

    Object a = new Object(); 

    System.out.println(x+1); 
} 
1

这只会令一些感在一些像这样的场景:

public void myHeavyMethod() { 
    List hugeList = loadHugeListOfStuff(); // lots of memory used 
    ResultX res = processHugeList(hugeList); // compute some result or summary 
    // hugeList = null; // we are done with hugeList 
    ... 
    // do a lot of other things that takes a LOT of time (seconds?) 
    // and which do not require hugeList 
    ... 
} 

这里它可能取得一些好处取消注释hugeList = null线,我想。

但重写方法肯定会更有意义(也许重构为两个, 或指定内部范围)。

+1

仅当虚拟机未实现JVM规范的版本3时。 – 2010-05-28 17:48:15

+0

能否详细说明一下? – leonbloy 2010-05-28 18:06:35

+0

请阅读我的回答中有关可达性的报价。如果processHughList没有存储对hugeList引用的对象的引用,那么它不能被任何潜在的持续计算从任何活动线程访问,因此无法访问,因此有资格进行垃圾收集。如果processHughList只使用大小和数据数组(假设List与ArrayList类似),并且这些被作为寄存器变量进行JIT,那么甚至可以在processHughList返回之前收集对象。 – 2010-05-28 22:16:34

7

从文章:

有一种情况下使用显式归零的不仅是有益的,但实际上需要的,那就是对一个对象的引用的范围限定更广泛的比它使用或根据程序的规范认为有效。这包括使用静态或实例字段来存储对临时缓冲区的引用(而非局部变量)或使用数组来存储运行时可以保持的引用,但不能隐含程序的语义的情况。

翻译:“显式为null”不再需要的持久对象。 (如果你想要的。“实际上,要求”太强大了一份声明?)

0

设置的对象引用只空使得资格进行垃圾回收。 它不一定释放内存,这取决于垃圾收集器运行的时间(取决于JVM)。 当垃圾收集器运行时,它通过删除合适的垃圾收集对象来释放堆。