to hiswing() :呵呵,我是说超出作用域就符合回收条件,至于什么时候回收还是看虚拟机的了。一般比较紧张的资源,我会在引用计数为零后调用System.gc(),主动回收。to 0xCafeBabe(咖啡宝贝~) :如果A代码所处的函数很长,在循环之后又作了好多事,那hash这个局部变量就要等好久才能超出作用域了。
但有一点我始终不清楚.请各位指教!! A. (1)在内存中声明了一个Hashtable,但并没有为其分配内存空间. (2)在for中为其分配内存空间.(这里,每次分配的是同一块内存空间吗?) B. (1)在for中声明Hashtable并为其分配内存空间.(这里,每次为其声明和分配的内存空间是否是不同的?)
答: A 不是。所以如果垃圾回收没有执行,将有10块内存。B 是不同的。每一次new都会分配一块内存。
我同意B。 因为 formalin(福尔马林) 讲的很明白了!!
还是看字节码清楚 package csdn;import java.util.*;public class Test1{ public void foo(){ int i; Hashtable hash; for (i = 0; i < 10; i++) { hash = new Hashtable(); } }
public void bar(){ int i; for (i = 0; i < 10; i++) { Hashtable hash = new Hashtable(); } } }public class csdn.Test1 extends java.lang.Object{ public csdn.Test1(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: returnpublic void foo(); Code: 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 22 8: new #2; //class Hashtable 11: dup 12: invokespecial #3; //Method java/util/Hashtable."<init>":()V 15: astore_2 16: iinc 1, 1 19: goto 2 22: returnpublic void bar(); Code: 0: iconst_0 1: istore_1 2: iload_1 3: bipush 10 5: if_icmpge 22 8: new #2; //class Hashtable 11: dup 12: invokespecial #3; //Method java/util/Hashtable."<init>":()V 15: astore_2 16: iinc 1, 1 19: goto 2 22: return} foo,bar两个方法的字节码居然是一样的!!所以,bar中的局部变量hash所指向的内存即使在循环结束后依然不能被垃圾回收,进一步,我认为在一个方法内部如何定义局部变量(在某些块内部或者不在)都不会对垃圾收集产生影响,这些变量所指向的内存都只有在方法结束后才有可能被垃圾收集。把hash定义在循环内,只会对编译器有影响,比如你在循环外再使用hash变量,那编译器就会报错。如果你想要bar中的变量hash所指向的内存在循环之后能被垃圾收集,你必须这样写 public void bar(){ int i; for (i = 0; i < 10; i++) { Hashtable hash = new Hashtable(); hash=null; } }
最后一句应该这样说:如果你想要bar中分配的最后一个Hashtable在循环之后能被垃圾收集,你必须这样写 public void bar(){ int i; for (i = 0; i < 10; i++) { Hashtable hash = new Hashtable(); hash=null; } } 或者你多判断一下 public void bar(){ int i; for (i = 0; i < 10; i++) { Hashtable hash = new Hashtable(); if(i==9) hash=null; } }
同意楼上因为在java中拉回收机制是程序员无法精确控制的如果程序员希望回收内存,只能将对象赋值为空
想知道区别吗?看下面的代码:public class Test1{ public void foo(){ int i; Hashtable hash; for (i = 0; i < 10; i++) { hash = new Hashtable(); } try { Thread.sleep(24*60*60*1000); } catch(Exception e) {} }
public void bar(){ int i; for (i = 0; i < 10; i++) { Hashtable hash = new Hashtable(); } try { Thread.sleep(24*60*60*1000); } catch(Exception e) {} } }A代码的最后一个hashtable的内存要在24小时之后才回收,而B代码最后一个hashtable的内存在任何java虚拟机需要的时候都可以回收。
to sniperlei(静水深流) 如果程序员希望回收内存,将对象赋值为空也没用。引用计数为0后,什么时候回收是看虚拟机的算法。文档里说调用System.gc(),可以建议回收。在我的实践中,调用System.gc(),百分百能够立即回收。
to formalin(福尔马林): 你的Test1中的foo和bar的字节码实际上是一样的,所以你所说的"A代码的最后一个hashtable的内存要在24小时之后才回收,而B代码最后一个hashtable的内存在任何java虚拟机需要的时候都可以回收。"其实是不对的,bar中最后一个hashtable也不可能立即回收,和foo的情况是一样的。
B 在经过循环之后 hash 不可访问
for(int i=0;i<99999;i++)
{
h=new Hashtable();
}
B: for(int i=0;i<99999;i++)
{
Hashtable h=new Hashtable();
}
A的垃圾回收
[GC 512K->123K(1984K), 0.2321493 secs]
[GC 635K->123K(1984K), 0.0043567 secs]
[GC 635K->123K(1984K), 0.0019620 secs]
[GC 635K->123K(1984K), 0.0009283 secs]
[GC 635K->123K(1984K), 0.0009401 secs]
[GC 635K->123K(1984K), 0.0009001 secs]
[GC 635K->123K(1984K), 0.0008929 secs]
[GC 635K->123K(1984K), 0.0011881 secs]
[GC 635K->123K(1984K), 0.0012488 secs]
[GC 635K->123K(1984K), 0.0009311 secs]
[GC 635K->123K(1984K), 0.0014379 secs]
[GC 635K->123K(1984K), 0.0013139 secs]
[GC 635K->123K(1984K), 0.0009208 secs]
[GC 635K->123K(1984K), 0.0012451 secs]
[GC 635K->123K(1984K), 0.0012611 secs]
[GC 635K->123K(1984K), 0.0009451 secs]
[GC 635K->123K(1984K), 0.0008811 secs]
[GC 635K->123K(1984K), 0.0009415 secs]
B的垃圾回收
[GC 512K->123K(1984K), 0.0581672 secs]
[GC 635K->123K(1984K), 0.0032968 secs]
[GC 635K->123K(1984K), 0.0019746 secs]
[GC 635K->123K(1984K), 0.0008568 secs]
[GC 635K->123K(1984K), 0.0012655 secs]
[GC 635K->123K(1984K), 0.0008169 secs]
[GC 635K->123K(1984K), 0.0007096 secs]
[GC 635K->123K(1984K), 0.0008225 secs]
[GC 635K->123K(1984K), 0.0007093 secs]
[GC 635K->123K(1984K), 0.0008445 secs]
[GC 635K->123K(1984K), 0.0010032 secs]
[GC 635K->123K(1984K), 0.0011549 secs]
[GC 635K->123K(1984K), 0.0011183 secs]
[GC 635K->123K(1984K), 0.0010328 secs]
[GC 635K->123K(1984K), 0.0011996 secs]
[GC 635K->123K(1984K), 0.0011035 secs]
[GC 635K->123K(1984K), 0.0011907 secs]
[GC 635K->123K(1984K), 0.0011228 secs]
大伙儿比较一下就知道了
for (int i = 0; i < 10; i++) {
.......
HashTable.clear();
}
HashTable.clear();
为
hash.clear();
for (int i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
......
hash = null; //变量即将超出作用域
}
而且循环结束后,没有残存对象。对于A,最好把代码加一句:
Hashtable hash = null;
for (int i = 0; i < 10; i++) {
hash = new HashTable();
.......
}
hash = null; //使最后一次循环产生的对象的引用计数器变为0,好让垃圾回收器回收。
2 编译器应该不会为b产生10个局部变量,所以栈桢所占内存和a是一样的。
3 至于垃圾回收,应该没有太大区别,因为a中的hash变量是一个局部变量,它即使在循环之后不置为null也没什么关系。
A.
(1)在内存中声明了一个Hashtable,但并没有为其分配内存空间.
(2)在for中为其分配内存空间.(这里,每次分配的是同一块内存空间吗?)
B.
(1)在for中声明Hashtable并为其分配内存空间.(这里,每次为其声明和分配的内存空间是否是不同的?)
A 不是。所以如果垃圾回收没有执行,将有10块内存。B 是不同的。每一次new都会分配一块内存。
因为 formalin(福尔马林) 讲的很明白了!!
package csdn;import java.util.*;public class Test1{
public void foo(){
int i;
Hashtable hash;
for (i = 0; i < 10; i++) {
hash = new Hashtable();
}
}
public void bar(){
int i;
for (i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
}
}
}public class csdn.Test1 extends java.lang.Object{
public csdn.Test1();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: returnpublic void foo();
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 22
8: new #2; //class Hashtable
11: dup
12: invokespecial #3; //Method java/util/Hashtable."<init>":()V
15: astore_2
16: iinc 1, 1
19: goto 2
22: returnpublic void bar();
Code:
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 22
8: new #2; //class Hashtable
11: dup
12: invokespecial #3; //Method java/util/Hashtable."<init>":()V
15: astore_2
16: iinc 1, 1
19: goto 2
22: return}
foo,bar两个方法的字节码居然是一样的!!所以,bar中的局部变量hash所指向的内存即使在循环结束后依然不能被垃圾回收,进一步,我认为在一个方法内部如何定义局部变量(在某些块内部或者不在)都不会对垃圾收集产生影响,这些变量所指向的内存都只有在方法结束后才有可能被垃圾收集。把hash定义在循环内,只会对编译器有影响,比如你在循环外再使用hash变量,那编译器就会报错。如果你想要bar中的变量hash所指向的内存在循环之后能被垃圾收集,你必须这样写
public void bar(){
int i;
for (i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
hash=null;
}
}
public void bar(){
int i;
for (i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
hash=null;
}
}
或者你多判断一下
public void bar(){
int i;
for (i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
if(i==9) hash=null;
}
}
public void foo(){
int i;
Hashtable hash;
for (i = 0; i < 10; i++) {
hash = new Hashtable();
}
try {
Thread.sleep(24*60*60*1000);
} catch(Exception e) {}
}
public void bar(){
int i;
for (i = 0; i < 10; i++) {
Hashtable hash = new Hashtable();
}
try {
Thread.sleep(24*60*60*1000);
} catch(Exception e) {} }
}A代码的最后一个hashtable的内存要在24小时之后才回收,而B代码最后一个hashtable的内存在任何java虚拟机需要的时候都可以回收。
你的Test1中的foo和bar的字节码实际上是一样的,所以你所说的"A代码的最后一个hashtable的内存要在24小时之后才回收,而B代码最后一个hashtable的内存在任何java虚拟机需要的时候都可以回收。"其实是不对的,bar中最后一个hashtable也不可能立即回收,和foo的情况是一样的。
http://expert.csdn.net/Expert/TopicView1.asp?id=2798638这样的话垃圾回收机制就很令人迷惑了,引用计数为零后还需要什么额外的条件才能满足回收呢?另外问一下,如何查看字节码?
最新的hotspot好像是用分代的方法,即对年轻对象采用拷贝算法,对老对象采用标记清除算法。
我人为 formalin(福尔马林) 讲的很对!
你可以参考他的例子,用你自己的方法!!!