下面是我的代码:
LinkedList <Mono> list = new LinkedList ();
list.add(new Mono (2, 2));
list.add(new Mono (1, -1));
list.remove (1);
现在,当列表中的第二项被删除时,对象是否被销毁?例如,它进行垃圾收集?
编辑新问题:
是,当不存在强引用时,对象将有资格进行垃圾收集。然而,JVM将尝试大量清理垃圾,因此实际上可以在以后的任意时间收集垃圾(或者如果JVM在GC处理垃圾之前终止,则永远不会收集垃圾)
老回答:
类卸载是一个罕见的事件,通常不会及时发生(如果有的话)
具体来说,即使在它符合收集条件之后,它也不会与"正常"的新对象(如Mono实例)一起被收集-它通常在一个特殊的不同池中(Oracle JVM中的PermGen)
你应该假设一旦一个类被加载,它将永远保持加载状态。一旦您进入容器中的web应用程序,这并不总是正确的,但是任何在这些环境中工作过的人都可以告诉您它通常工作得如何(不)。
Java中的垃圾收集通常是不确定的,就何时发生以及当GC周期发生时GC将驱逐哪些符合条件的对象("free") 而言。
唯一可靠的规则是:
- 只要对象强可达,它就保持可用(不会被GC'ed/释放/驱逐)。(请参阅《Java虚拟机内部》第9章中的"对象的可达性生命周期",了解这是什么意思——总的来说,这是一篇很好的阅读,如果有点过时的话。)
在发布的代码中,remove
将导致第二个对象new Mono(1, -1)
符合回收条件,因为不再有任何对它的强引用(对象不再是强可及的)。实际的驱逐(如果发生的话)将在"稍后"发生,甚至可能不是下一个GC周期。使用终结器(ick)使问题更加复杂。
请注意,对象永远不能保证被GC(例如,JVM可能只是正常终止[ab]),特定GC实现的确切语义可以不同,但仍然是一个符合标准的虚拟机——这一切都归结为可达性。
幸福的编码。
你是说当Object被卸载时?
空列表仍然是一个列表,因此它将留在内存中。当您执行
时,它将被清除。列表=换掉;
这是假设你没有给list赋值任何东西
就类定义本身而言,它应该永远留在内存中。类定义在永久代中。
作为旁注。列表在此时不能被垃圾收集。因为你可以在清除后往里面添加东西。
第二个对象在从列表中删除后将有资格进行垃圾收集,因为不再有对它的引用。因此超出了范围。
希望这对你有所帮助
答案很多,所以这里是我的观点。想想"作用域",这是计算机语言中的一个概念,它描述了你可以在何时何地访问指定的内存位。
这是您的原始代码,添加了所谓的删除第二个列表成员:
LinkedList <Mono> list = new LinkedList ();
list.add(new Mono (2, 2));
list.add(new Mono (1, -1));
list.remove (1);
list.remove (2);`
在list.remove(2)点,对象"list"仍然可以被引用。当然,它是空的,但随后您可能会决定添加一个新的单声道。因为"list"仍然在范围内,所以"list"不会被回收。
与此比较:
{
LinkedList <Mono> list = new LinkedList ();
list.add(new Mono (2, 2));
list.add(new Mono (1, -1));
list.remove (1);
list.remove (2);`
}
在结束大括号之后,"list"不能再被引用。"list"是在该范围内声明的,当退出该范围时,"list"将与范围本身一起从名称空间中删除。在那个点,垃圾收集可能会发生,因为没有人可能会再次使用"list"。
注意对象的作用域。如果希望影响垃圾收集,请将对象的作用域限制在使用它们的区域。碰巧,这也是一种很好的编程风格。
我猜人们对你使用的术语感到困惑。我相信你是在问你的Mono
对象是否会被"删除"/"垃圾收集"。
让我们看一下您正在调用的remove(1)
…
这是您正在调用的删除函数,定义在java.util.LinkedList
:
public E remove(int index) {
return remove(entry(index));
}
上面的函数调用了以下代码(查看我在代码中的注释):
private E remove(Entry<E> e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next; //Preceding element refers now to the one after the element to be removed;
e.next.previous = e.previous; //Next element refers now to the one before the one to be removed;
e.next = e.previous = null; //Element to be removed doesn't refer to anything anymore;
e.element = null;
size--;
modCount++;
return result;
}
在你调用的函数终止后,就没有办法再引用你的Mono(1, -1)了。那个单对象不再是可访问的了。这意味着它将符合垃圾收集的条件。请记住,"合格"可能意味着它永远不会被垃圾收集……
简单的答案是,Java对象何时被垃圾收集对您来说应该无关紧要。您唯一需要知道的是,它将被垃圾收集在您的程序耗尽内存…提供对象不可达。
复杂的答案包括:
-
垃圾收集器通常在您无法预测的时间运行。
-
您可以调用
System.gc()
来建议JVM现在运行GC,但是:-
JVM可能会忽略这个提示,而
-
这样做通常是个坏主意。(运行GC是昂贵的,并且您的应用程序没有足够的信息来知道从效率的角度来看什么时候执行GC是最好的。)
-
-
任何特定的GC运行都不能保证回收所有不可达的对象。GC有很多"智能",旨在使GC尽可能高效,或减少"暂停"时间。其中一个"聪明"是不要每次都对整个堆进行GC。
-
不能保证GC会运行,也不能保证它会在JVM关闭之前运行。
-
由于上述原因,编写一个依赖于在特定时间被回收/删除的特定对象的应用程序是坏主意。
在内存管理中,应该关注的一件事是存储泄漏;例如,当你的应用程序保持对不需要的对象的引用,以防止这些对象变得不可访问。
类"Mono"不能卸载,因为仍然有对它的引用。列表的类型引用了它,列表中仍然有一个元素。
我想你不是想问类是否被卸载了,而是想问实例是否被"卸载"了。类的每个实例、每个对象都是在堆上分配的。当对象不再使用时,可以回收它在堆中占用的空间。然而,这不会立即发生。我所知道的所有JVM实现都使用垃圾收集器来清理内存。为了真正简化这里的事情:当没有更多的空间可以在堆上创建新对象时,垃圾收集器将启动并检查堆的哪些部分仍在使用。不再使用的部件可以重新用于新对象。
因此,对象不再使用的内存只会在垃圾收集器启动时被回收。这是无法预测的。
您的意思是Mono的实例有资格进行垃圾收集还是list的实例有资格进行垃圾收集?
当mono的实例被删除时,它将有资格被垃圾收集(假设代码没有创建对它的引用)。
列表不适合垃圾收集,因为它是空的。空列表不能被垃圾收集,因为它是一个可以再次读写的有效对象。
正如其他人指出的那样。我们讨论的是垃圾收集的资格。垃圾回收器不需要立即运行