null
在Java中分配未使用的对象引用是否可以以任何可衡量的方式改善垃圾回收过程?
我在Java(和C#)方面的经验告诉我,尝试超越虚拟机或JIT编译器通常是反直观的,但是我看到同事使用这种方法,我很好奇这是否是一个好的选择或其中一种巫术编程迷信?
Answers:
通常没有。
但是像所有事物一样:这取决于。如今,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;
}
在功能上是等效的。
System.gc()
,它将被删除(前提是没有其他指向该对象的引用)。
以我的经验,人们常常出于偏执狂而不是出于必要而取消引用。这是一个快速指南:
如果对象A引用了对象B,并且您不再需要该引用,并且对象A不符合垃圾回收的条件,则应显式将字段清空。如果封闭的对象无论如何都要收集垃圾,则无需清空字段。在dispose()方法中淘汰字段几乎总是无用的。
无需清空在方法中创建的对象引用。一旦方法终止,它们将自动清除。该规则的例外情况是,如果您正在非常长的方法中运行或某些大规模循环运行,并且需要确保在方法结束之前清除某些引用。同样,这些情况极为罕见。
我要说的是,在绝大多数情况下,您将不需要删除引用。试图超越垃圾收集器是没有用的。您将最终得到效率低下,无法读取的代码。
好文章是当今的编码恐怖。
GC的工作方式是查找没有指向它们的指针的对象,它们的搜索区域是堆/堆栈以及它们拥有的任何其他空间。因此,如果将变量设置为null,则任何人现在都无法指向实际对象,因此可以进行GC处理。
但是,由于GC可能无法在那个确切的时刻运行,因此您实际上可能并没有购买任何东西。但是,如果您的方法相当长(就执行时间而言),则可能值得这样做,因为您将增加GC收集该对象的机会。
代码优化也会使问题变得复杂,如果您在将变量设置为null之后再也不使用变量,那么删除将值设置为null的行(减少执行一条指令)将是一种安全的优化方法。因此,您实际上可能没有任何改善。
因此,总而言之,是的,它可以提供帮助,但不是确定性的。
至少在Java中,这根本不是伏都教编程。当您使用类似的方式在Java中创建对象时
Foo bar = new Foo();
您要做两件事:首先,创建对对象的引用,其次,创建Foo对象本身。只要该引用或另一个引用存在,就不能gc'd特定对象。但是,当您为该引用分配null时...
bar = null ;
并假设没有其他对象对该对象的引用,则该对象将在下一次垃圾收集器通过时释放并可供gc使用。
明确地将引用设置为null而不是仅仅让变量超出范围不会对垃圾收集器有所帮助,除非所保存的对象非常大,这是个好主意,将对象设置为null即可。
通常将引用设置为null,表示此对象已完全完成且不再关注的代码的READER。
通过引入额外的括号来引入更窄的范围,可以达到类似的效果
{
int l;
{ // <- here
String bigThing = ....;
l = bigThing.length();
} // <- and here
}
这样就可以在离开嵌套括号之后立即对bigThing进行垃圾收集。
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
我认为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处理。
null
影响GC行为。
“这取决于”
我不了解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
在将来执行程序时,某些数据成员的值将用于计算机输出程序外部可见的输出。可能使用或可能不使用其他工具,这取决于将来(且无法预测)该程序的输入。可以保证不使用其他数据成员。分配给那些未使用的数据的所有资源(包括内存)都被浪费了。垃圾收集器(GC)的工作是消除浪费的内存。GC消除所需的内容将是灾难性的,因此所使用的算法可能是保守的,保留的数据超过了严格的最低要求。它可能会使用启发式优化来提高其速度,但会保留一些实际不需要的项。GC可能使用许多潜在的算法。因此,您可能会对程序进行更改,并且不会影响程序的正确性,但是可能会影响GC的运行,使其运行得更快以完成相同的工作,或者更快地识别未使用的项目。所以这种改变,设置了一个unusdd对象引用null
,理论上并不总是巫毒教。
是伏都教吗?据报道,Java库代码的某些部分可以执行此操作。该代码的编写者比普通程序员要好得多,并且他们知道或与了解垃圾收集器实现细节的程序员合作。所以这表明有是有时好处。
如您所说,存在优化,即JVM知道上次使用变量的位置,并且可以在最后一点之后立即对GC引用的对象进行GC(仍在当前作用域中执行)。因此,在大多数情况下取消引用对GC毫无帮助。
但是,避免“裙带关系”(或“浮动垃圾”)问题可能很有用(请在此处阅读更多信息或观看视频)。存在此问题是因为堆分为老一代和年轻一代,并且应用了不同的GC机制:次要GC(快速且经常发生在清理年轻一代)和主要Gc(导致清理老一代的暂停时间更长)。如果“裙带关系”被已经存在于老一代中的垃圾所引用,则不允许收集年轻一代中的垃圾。
这是“病态的”,因为任何提升的节点都将导致提升所有后续节点,直到GC解决问题。
为了避免裙带关系,最好从应该被删除的对象中删除引用。您可以看到在JDK类中应用了此技术:LinkedList和LinkedHashMap
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
// ...
}