我是一名进入Java世界的C ++程序员。而且我无法摆脱不得不让Java垃圾收集器进行清理的坏感觉。
例如,这段代码如何在Java中运行?
public void myFunction() {
myObject object = new myObject();
object.doSomething();
}
当myFunction()退出时,是否会删除局部变量对象?
我必须在退出之前将对象设置为null,还是超出范围并被GC删除?或者,在最坏的情况下,它会像在C ++中一样泄漏吗?
答案 0 :(得分:26)
在不再使用之后的某个时刻,它将被垃圾收集。我相信Java的当前实现它实际上会持续到方法结束,而.NET中的垃圾收集器更具侵略性。 (我不知道是否有任何保证,即使在Java中。通常你只需希望本地变量在你调试时保持超出其最后可能的读数。)
但不,你不需要将变量设置为null,这样做会损害可读性。
在方法退出后,对象不太可能立即回收 ;这取决于GC运行的时间......当然,如果其他任何东西保留在对象的引用上,它可能无法进行垃圾收集。不要忘记变量的值只是一个引用,而不是对象本身。 (这可能需要一段时间才能习惯来自C ++。)
答案 1 :(得分:2)
GC至少在内存限制接近时完成工作,因为超出范围的变量引用的任何对象都可以被垃圾收集,但是最后一个退出块的超出范围的局部变量存在意外行为。
要深入了解,让我们进行比较:
{
final List myTooBigList = new ArrayList();
... overfill the list
}
somethingRunOutOfMemory();
somethingRunOutOfMemory()
因为myTooBigList
不是GCable,尽管不在范围内且声明了更多内存,不像以下内容:
{
final List myTooBigList = new ArrayList();
... overfill the list
}
Object fake = null;
somethingDoesNotRunOutOfMemory();
fake
的影响将堆栈指针移回并让myTooBigList
GCable,然后GC在内存限制接近时立即执行其工作。
令人惊讶的是(至少在我正在测试的jvm中)我们必须明确地在同一级别重用堆栈。一旦块退出,可以预期局部变量是GCable,但我想这是对性能的妥协。它会使字节码复杂化。
与C类似,局部变量位于帧旁边的堆栈中。 堆栈指针保留了范围内局部变量所需的空间。 当重用堆栈时,{exited block}的局部变量变为GCable,即:在同一堆栈级别,函数返回后或退出评估(catch,循环条件)之后声明局部变量之后。
他们是GCable之后:
try { } catch : after exit by catch because catch reuses stack
for { } : after exit loop condition because evaluation reuses stack
while { } : after exit loop condition because evaluation reuses stack
{ } declare=value : after any declaration+affectation that reuses stack
它们之后才是GCable:
try { } : after nothing caught
for { } : after exit by break
while { } : after exit by break
do { }
if { }
{ }
注意:对于实验室,请运行GC,然后将WeakReference(my variable)
与null
进行比较。
final WeakReference gctest;
{
final List myTooBigList = new ArrayList();
gctest = new WeakReference(myTooBigList);
... overfill the list
}
Object fake = null;
System.gc();
assert gctest.get() == null;
答案 2 :(得分:1)
它将超出范围。在Java中,当没有人再指向一个对象时,它将被垃圾收集,或者至少它可用于垃圾收集。这里不需要将其设置为null。如果您的对象将存在于App中,则有时需要将对象引用设置为null,但是它所持有的引用需要进行垃圾回收。在这种情况下,您选择发布参考。