关于Java垃圾收集器、null和内存泄漏的问题
假设我正在用java实现一个队列,我有一个对初始节点的引用,称为ini,另一个对最后一个节点的引用,称为last。现在,我开始向队列中插入对象。有一次,我决定执行一个操作来清除队列。然后我会这样做:关于Java垃圾收集器、null和内存泄漏的问题,java,garbage-collection,reference,Java,Garbage Collection,Reference,假设我正在用java实现一个队列,我有一个对初始节点的引用,称为ini,另一个对最后一个节点的引用,称为last。现在,我开始向队列中插入对象。有一次,我决定执行一个操作来清除队列。然后我会这样做: ini = null; last = null; 我在泄漏内存吗?ini和last之间的节点都是链接的,仍然有数据,我猜,但同时还有垃圾收集器 另一种方法是访问每个元素,然后将它们的引用归为下一个节点,但之后我基本上像C++那样做,除非我不明确地使用DELL.< P>,只要队列中没有任何项引用到代
ini = null;
last = null;
我在泄漏内存吗?ini和last之间的节点都是链接的,仍然有数据,我猜,但同时还有垃圾收集器
另一种方法是访问每个元素,然后将它们的引用归为下一个节点,但之后我基本上像C++那样做,除非我不明确地使用DELL.
< P>,只要队列中没有任何项引用到代码中的任何地方,垃圾回收器就能够回收该内存。在Java中将指针设置为null与在C中不同,在C中,将malloc'ed指针设置为null会阻止释放它。在Java中,当内存不再可访问时,就会回收内存。Java中没有内存泄漏(在C/C++的意义上),只要您没有通过JNI使用本机代码 一个过于简单的垃圾收集器只会计算对一个对象的引用数,并在引用数为零时释放该对象,但这将无法处理引用周期(A->B、A->B->C->A等)。JavaGC算法进行活动性测试,在测试中,它们构建系统中所有对象的参考图。GC进行图形遍历,任何不可访问的节点(对象)都被标记为未使用且可重新分配。图的根(遍历的起始点)包括线程堆栈上的变量、静态变量和本机代码通过JNI持有的引用。请参阅此处的更多信息: 仍然可能存在参考泄漏。这指的是您对对象的引用的保留时间超过需要的时间的情况。例如:public class Stack {
private final Object[] stack = new Object[10];
private int top = 0;
public void push(Object obj) {stack[top++] = obj;}
public Object pop() {return stack[top--]; }
}
忽略溢出/下溢的可能性,在调用Stack.pop()之后,数组成员变量仍然具有对返回对象的引用。它将防止对象被垃圾收集,直到周围的堆栈实例不再被引用。这是极少数需要将变量设置为null以回收其内存的情况之一:
public Object pop() {Object ret = stack[top]; stack[top--] = null; return ret;}
那很好。GC将检测到节点不可访问,因此它们都将被清除。以下是一些代码,用于演示列表结构中间的错误句柄如何阻止GC完全清除:
import java.lang.ref.*;
public class MemoryLeak1 {
MyListItem leakedItem = null;
WeakReference[] refs = null;
public static void main(String[] args) {
WeakReference ref = null;
MyListItem item = null;
MemoryLeak1 leak = new MemoryLeak1();
int i;
leak.doit(); // create a memory leak
System.gc(); // force the gc to run;
// At this point the list has been explicitly cleared,
// has gone out of scope, and the GC has run.
// However, leak.leakedItem is still holding a
// reference to an item in the list, so anything
// reachable from that item is still alive.
// show what's still around...
for (i = 0; i < 10; i++) {
ref = leak.refs[i];
item = (MyListItem)ref.get();
if (item == null) { System.out.println("" + i + " = null"); }
else { System.out.println("" + i + " = " + (String)item.thing); }
}
System.out.println("---------------------");
// now let's free some additional items...
for (i = 1; i <= 3; i++) {
item = leak.leakedItem;
leak.leakedItem = item.next;
leak.leakedItem.prev = null;
item.prev = null;
item.next = null;
}
item = null;
System.gc(); // force the gc to run again
// this time we should get fewer items
for (i = 0; i < 10; i++) {
ref = leak.refs[i];
item = (MyListItem)ref.get();
if (item == null) { System.out.println("" + i + " = null"); }
else { System.out.println("" + i + " = " + (String)item.thing); }
}
System.out.println("---------------------");
// now clear the last reference
leak.leakedItem = null;
System.gc(); // force the gc to run again
// this time we should none
for (i = 0; i < 10; i++) {
ref = leak.refs[i];
item = (MyListItem)ref.get();
if (item == null) { System.out.println("" + i + " = null"); }
else { System.out.println("" + i + " = " + (String)item.thing); }
}
System.out.println("---------------------");
}
public void doit() {
this.refs = new WeakReference[10];
MyList list = new MyList();
MyListItem item = null;
// add strings to the list.
// set each into the array of soft refs
// set a ptr to the 6th item in an instance variable
for (int i = 0; i < 10; i++) {
item = new MyListItem();
item.thing = new String("string" + i);
list.insert(item);
if (i == 5) this.leakedItem = item;
this.refs[i] = new WeakReference(item);
}
// clear the list, but don't clear the
// additional ptr to the 6th item
list.clear();
}
}
class MyList {
MyListItem head = null;
MyListItem tail = null;
void clear() {
head = null;
tail = null;
}
void insert(MyListItem item) {
if (head == null) {
// empty list
item.next = null;
item.prev = null;
tail = item;
head = item;
}
else if (head == tail) {
// one item in list
item.next = head;
item.prev = null;
tail = head;
head = item;
}
else {
// multiple items in list
item.next = head;
item.prev = null;
head = item;
}
}
MyListItem remove() {
MyListItem item = tail;
if (item != null) {
tail = item.prev;
if (tail == null) {
head = null;
}
else {
tail.next = null;
}
item.next = null;
item.prev = null;
}
return item;
}
}
class MyListItem {
MyListItem next = null;
MyListItem prev = null;
Object thing = null;
}
import java.lang.ref.*;
公共类MemoryLeak1{
MyListItem leakedItem=null;
WeakReference[]refs=null;
公共静态void main(字符串[]args){
WeakReference ref=null;
MyListItem项=null;
MemoryLeak1泄漏=新的MemoryLeak1();
int i;
leak.doit();//创建内存泄漏
System.gc();//强制gc运行;
//此时,列表已被明确清除,
//已超出范围,并且GC已运行。
//但是,leak.leakedItem仍然持有
//引用列表中的某个项目,所以
//可从该项访问的对象仍处于活动状态。
//展示仍然存在的东西。。。
对于(i=0;i<10;i++){
ref=泄漏。refs[i];
item=(MyListItem)ref.get();
如果(item==null){System.out.println(“+i+”=null”);}
else{System.out.println(“+i+”=“+(String)item.thing);}
}
System.out.println(“--------------------------”;
//现在让我们释放一些额外的项目。。。
对于(i=1;iYes),GC在这种情况下工作。
但头部和尾部之间的元素可能会存活下来,然后进入老一代的空间,然后
它们将在完全GC期间收集。
正如您所知,完全GC是昂贵的。就性能而言,将其置零更好
您可以看到java.util.LinkedList的clear()方法是如何实现的
public void clear() {
Entry<E> e = header.next;
while (e != header) {
Entry<E> next = e.next;
e.next = e.previous = null;
e.element = null;
e = next;
}
header.next = header.previous = header;
size = 0;
modCount++;
}
public void clear(){
条目e=标题。下一步;
while(e!=标题){
条目next=e.next;
e、 next=e.previous=null;
e、 元素=空;
e=下一个;
}
header.next=header.previous=header;
尺寸=0;
modCount++;
}
触及问题。如果您怀疑内存泄漏,我建议您使用内存探查器查看对象在一段时间内是如何被保留的。使用这种工具,快速内存泄漏将是显而易见的,因此如果您为怀疑泄漏的内容创建测试并重复多次,您将能够看到泄漏以及对象被保留的原因etained.缺少节点的其他PTR是关键。如果任何节点都可以访问,它们都可以访问(假设列表中的下一个和上一个PTR)clayton,真的吗?这意味着它们无法访问,但它们仍然不会被垃圾收集。我不知道,但这听起来很奇怪。这是否更像是如果对对象的最后一个直接或间接堆栈引用丢失,那么对象就被释放了?litb:如果你对列表中的某个项有ptr,那么e列表项都与下一个/上一个PTR链接在一起,将列表的开头和结尾设置为null并不会使它们无法访问,因为您仍然有一个链接到列表的“中间”。litb:我认为这就是为什么在实现链接列表时,您没有将列表中的实际项链接在一起的原因。您使用了一个特殊的“ListItem”对象,并让它指向存储在那里的对象。只要不保留ListItem对象的ptr,就可以了。System.gc()不会强制垃圾收集器运行。Bombe:好的,“force”可能不是正确的词,但这里来自api文档:“调用gc方法表明[JVM]努力回收未使用的对象……当控制从方法调用返回时,[JVM]已尽最大努力回收sp