锁定老帖子 主题:Java中的垃圾回收与对象生命周期
精华帖 (5) :: 良好帖 (2) :: 新手帖 (5) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-22
最后修改:2010-06-22
wangzaixiang 写道 晚上再看看帖子,略有感触。
1、这个初哥不理解我的问题也就罢了,放屁的词语也出来了。现在,不知道到底明白是我在放屁还是楼主搞错了。 2、谢谢mercyblitz替我回复,与我的表述基本相符,但可能存在笔误,我再修订一下。 每一行执行代码称为一帧 =》 应为每一次函数调用对应一帧。 确实没有差别,理由:这两代码都生成了10000个对象,楼主对引用的理解有误。第二段代码中,相对与第一段而言,少开辟了几个栈对象,也就是说 Object obj只是个一个标示,关键是new了10000次,因此在执行效率和内存消耗没有不同,只是变量的作用域变化了其实,第二段代码和第一段代码在帧上分配的变量是一样的,也是一个栈变量(不合适成为栈对象)。 1. 请问,如果考虑GC的话,哪个比较快呢? 2. 我可以这么认为么?一个函数对应了一帧,所以2个的帧数是一样的,所以2者的效率一样?不过我javap -c 看了一下,两者还是有区别的 我用while代替了for,这样更容易看 while(true){ Object obj = new Object(); System.out.println(obj); } FRAME SAME Object obj = null; while(true){ obj = new Object(); System.out.println(obj); } FRAME APPEND [java/lang/Object] 这两者的Frame是不同的,请问跟你说的是不是违背呢?? 引用 4、当对象处于释放状态后,空间状态为“已被重用”,这个是典型的字面翻译,其错误不值一提。 感觉楼主应该是一个很好学的程序员,不过,目前,Java水平还处在幼儿园级别,谦虚程度又颇有博士生的感觉。谈到放屁二字,我稍微吹吹水,在JVM上,我小有10余年研究,不算专家的话,也是略有精通的人了。本想指点楼主一二,却可惜被反批了一顿,笑笑飘过。 能否给出您的理解呢?? |
|
返回顶楼 | |
发表时间:2010-06-22
最后修改:2010-06-22
beneo 写道 1. 请问,如果考虑GC的话,哪个比较快呢? 2. 我可以这么认为么?一个函数对应了一帧,所以2个的帧数是一样的,所以2者的效率一样?不过我javap -c 看了一下,两者还是有区别的 我用while代替了for,这样更容易看 while(true){ Object obj = new Object(); System.out.println(obj); } FRAME SAME Object obj = null; while(true){ obj = new Object(); System.out.println(obj); } FRAME APPEND [java/lang/Object] 这两者的Frame是不同的,请问跟你说的是不是违背呢?? 1、即便考虑GC,上述的两段代码效率仍然是一样的。这里的一样是100%的一样。 2、从java字节码来看,二者确实是有区别的。主要是obj的作用域,第一段代码更短,第二段代码则要长一些。但是作用域主要是一个语法的范畴,和GC关系并不大。(不是说完全没有关系,比如说,第一段代码理论上你无需obj = null,在离开循环后,即可成为垃圾。而后者则需要:手动设为null或者函数执行结束后,才能成为垃圾。但无论在何种情况下,只有一个对象可能会被引用到,其它循环之前的对象在循环进入到下一轮之前已经成为垃圾。 |
|
返回顶楼 | |
发表时间:2010-06-22
wangzaixiang 写道 beneo 写道 1. 请问,如果考虑GC的话,哪个比较快呢? 2. 我可以这么认为么?一个函数对应了一帧,所以2个的帧数是一样的,所以2者的效率一样?不过我javap -c 看了一下,两者还是有区别的 我用while代替了for,这样更容易看 while(true){ Object obj = new Object(); System.out.println(obj); } FRAME SAME Object obj = null; while(true){ obj = new Object(); System.out.println(obj); } FRAME APPEND [java/lang/Object] 这两者的Frame是不同的,请问跟你说的是不是违背呢?? 1、即便考虑GC,上述的两段代码效率仍然是一样的。这里的一样是100%的一样。 2、从java字节码来看,二者确实是有区别的。主要是obj的作用域,第一段代码更短,第二段代码则要长一些。但是作用域主要是一个语法的范畴,和GC关系并不大。(不是说完全没有关系,比如说,第一段代码理论上你无需obj = null,在离开循环后,即可成为垃圾。而后者则需要:手动设为null或者函数执行结束后,才能成为垃圾。但无论在何种情况下,只有一个对象可能会被引用到,其它循环之前的对象在循环进入到下一轮之前已经成为垃圾。 那么bytecodek后我所看到的FRAME APPEND和FRAME SAME是不是你们一直谈论的“帧”,如果是的话,我觉得您说的话可能有不对的地方。 |
|
返回顶楼 | |
发表时间:2010-06-22
最后修改:2010-06-22
你把javap -c 的代码贴出来吧。再给你解释。
|
|
返回顶楼 | |
发表时间:2010-06-22
最后修改:2010-06-22
wangzaixiang 写道 你把javap -c 的代码贴出来吧。再给你解释。
Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #3; //class java/lang/Object 3: dup 4: invokespecial #8; //Method java/lang/Object."<init>":()V 7: astore_1 8: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream; 11: aload_1 12: invokevirtual #22; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 15: goto 0 第二种 Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: aconst_null 1: astore_1 2: new #3; //class java/lang/Object 5: dup 6: invokespecial #8; //Method java/lang/Object."<init>":()V 9: astore_1 10: getstatic #16; //Field java/lang/System.out:Ljava/io/PrintStream; 13: aload_1 14: invokevirtual #22; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 17: goto 2 |
|
返回顶楼 | |
发表时间:2010-06-22
第二种的差别仅仅是多了一条 obj = null的指令罢了。就是 aconst_null, astore_1,除此之外,和第一个版本是一样的。
|
|
返回顶楼 | |
发表时间:2010-06-22
最后修改:2010-06-22
wangzaixiang 写道 第二种的差别仅仅是多了一条 obj = null的指令罢了。就是 aconst_null, astore_1,除此之外,和第一个版本是一样的。
你好,我后来用eclipse插件看,http://andrei.gmxhome.de/bytecode/index.html 第一种是frame same,第二种是frame append 参考 3.15 frame, http://download.forge.objectweb.org/asm/asm-guide.pdf 如果我的猜测没有错的话,第一种比第二种还要快,你看看 // class version 50.0 (50) // access flags 0x21 public class com/tencent/BytecodeUnitTests { // compiled from: BytecodeUnitTests.java // access flags 0x1 public <init>()V L0 LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init>()V RETURN L1 LOCALVARIABLE this Lcom/tencent/BytecodeUnitTests; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 6 L0 FRAME SAME NEW java/lang/Object DUP INVOKESPECIAL java/lang/Object.<init>()V ASTORE 1 L1 LINENUMBER 7 L1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/Object;)V L2 LINENUMBER 5 L2 GOTO L0 L3 LOCALVARIABLE args [Ljava/lang/String; L0 L3 0 LOCALVARIABLE obj Ljava/lang/Object; L1 L2 1 MAXSTACK = 2 MAXLOCALS = 2 } // class version 50.0 (50) // access flags 0x21 public class com/tencent/BytecodeUnitTests { // compiled from: BytecodeUnitTests.java // access flags 0x1 public <init>()V L0 LINENUMBER 3 L0 ALOAD 0 INVOKESPECIAL java/lang/Object.<init>()V RETURN L1 LOCALVARIABLE this Lcom/tencent/BytecodeUnitTests; L0 L1 0 MAXSTACK = 1 MAXLOCALS = 1 // access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 5 L0 ACONST_NULL ASTORE 1 L1 LINENUMBER 7 L1 FRAME APPEND NEW java/lang/Object DUP INVOKESPECIAL java/lang/Object.<init>()V ASTORE 1 L2 LINENUMBER 8 L2 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/Object;)V L3 LINENUMBER 6 L3 GOTO L1 L4 LOCALVARIABLE args [Ljava/lang/String; L0 L4 0 LOCALVARIABLE obj Ljava/lang/Object; L1 L4 1 MAXSTACK = 2 MAXLOCALS = 2 } |
|
返回顶楼 | |
发表时间:2010-06-22
mercyblitz 写道 maozj 写道 wangzaixiang 写道 写了很多,错误不少。
1. 而栈内存则是用来存储程序代码中声明为静态或非静态的方法。 完全错误的表达。 2、为对象分配存储空间; <2> 开始构造对象; <3> 从超类到子类对static成员进行初始化; <4> 超类成员变量按顺序初始化,递归调用超类的构造方法; <5> 子类成员变量按顺序初始化,子类构造方法调用。 static的初始化与new操作没有任何关系。 3、比如: 不要写成: for (int i = 0; i < 10000; i++) { Object obj = new Object(); System.out.println(obj); } 要写成: Object obj = null; for (int i = 0; i < 10000; i++) { obj = new Object(); System.out.println(obj); } 完全错误。两者在运行时几无差别,前置在编码风格上更为合理。 4、 (5) 可收集阶段、终结阶段与释放阶段 当一个对象处于可收集阶段、终结阶段与释放阶段时,该对象有如下三种情况: <1> 回收器发现该对象已经不可达。 <2> finalize方法已经被执行。 <3> 对象空间已被重用。 前面的理解都不正确,这里的表述更为问题多多。 1. 而栈内存则是用来存储程序代码中声明为静态或非静态的方法。 完全错误的表达。 请您解释下? 2. static的初始化与new操作没有任何关系。放屁 3.完全错误。两者在运行时几无差别,前置在编码风格上更为合理。 理由? 4. 请解释。 呵呵,我来解释吧,首先楼主,你要明白一点,wangzaixiang说的是对的。为什么? 1.栈内存是由栈帧(frame)组成的,在Java代码中的,每一行执行代码称为一帧。比如Object obj=null;obj这样的标示对象会保存在栈里面。同时,方法参数对象标示也在栈中。对象的实际物理保存都在堆中。由于Java的重进入, 所以栈对象是线程安全,而堆是JVM线程共享的,因此需要合理的同步。 2.确实没有关系,static成员隶属于类对象,类的static成员的初始化是在ClassLoader第一次加载该类的时候。和new确实没有什么关系,只不过第一次出现new SomeObject的时候,会加载SomeObject,然后静态成员初始化了。楼主这里就误会了,不一定new 才是加载类的开始,通过调用类的静态可访问的方法也是可以的。 3.确实没有差别,理由:这两代码都生成了10000个对象,楼主对引用的理解有误。第二段代码中,相对与第一段而言,少开辟了几个栈对象,也就是说Object obj只是个一个标示,关键是new了10000次,因此在执行效率和内存消耗没有不同,只是变量的作用域变化了。 4.我的理解是: <1> 比如方法中的局部对象,并不需要JVM标记不可达,在方法执行完之后,马上就被回收。这就是为什么不需要在局部对象使用后,显示地设置为null. <2>finalize 不一定可靠,或者被调用。规范中没有规定,垃圾收集器必须要在对象回收时,调用这种方法或者并不是每个对象回收时均被调用。不等同于free或者C++的析构。 <3>重用是肯定的,但是对象空间能不能马上重用,很难说,G1的算法和CMS等算法不同。G1是等大小区域,相对来说比较容易。CMS,我没有记错的话,是采用的碎片空间移动。 -------------------------- 这样的表述可以完全接受,如果看完帖子你能给出这样的答案,那我不枉发布此贴~~ 谢谢. |
|
返回顶楼 | |
发表时间:2010-06-22
wangzaixiang 写道 晚上再看看帖子,略有感触。
1、这个初哥不理解我的问题也就罢了,放屁的词语也出来了。现在,不知道到底明白是我在放屁还是楼主搞错了。 2、谢谢mercyblitz替我回复,与我的表述基本相符,但可能存在笔误,我再修订一下。 每一行执行代码称为一帧 =》 应为每一次函数调用对应一帧。 引用 确实没有差别,理由:这两代码都生成了10000个对象,楼主对引用的理解有误。第二段代码中,相对与第一段而言,少开辟了几个栈对象,也就是说 Object obj只是个一个标示,关键是new了10000次,因此在执行效率和内存消耗没有不同,只是变量的作用域变化了。
其实,第二段代码和第一段代码在帧上分配的变量是一样的,也是一个栈变量(不合适成为栈对象)。 4、当对象处于释放状态后,空间状态为“已被重用”,这个是典型的字面翻译,其错误不值一提。 感觉楼主应该是一个很好学的程序员,不过,目前,Java水平还处在幼儿园级别,谦虚程度又颇有博士生的感觉。谈到放屁二字,我稍微吹吹水,在JVM上,我小有10余年研究,不算专家的话,也是略有精通的人了。本想指点楼主一二,却可惜被反批了一顿,笑笑飘过。 呵呵,你发展下去肯定会达到项目经理,很有项目经理的潜质 |
|
返回顶楼 | |
发表时间:2010-06-22
wangzaixiang 写道 maozj 写道 joinhack 写道 for (int i = 0; i < 10000; i++) {
Object obj = new Object(); System.out.println(obj); } 要写成: Object obj = null; for (int i = 0; i < 10000; i++) { obj = new Object(); System.out.println(obj); } 大家都感觉作者说的是错误的,但是并没有说出为什么,因此作者毛了,呵呵 其实,作者的意思是这么写会造成很多引用留在栈里面,但是作者还有个东西没有考虑,就是作用域。 句柄会随着作用域消失而消失,因此上面的2种写法,对于句柄来说不会不断扩大,作者可以放心使用。 恩 成很多引用留在栈里面 说的正确。。。 Object obj = null; 后者还多了一条初始化 您的说法 完全接受。。。 表述不错 足见很有方法哲理~~ 虽然会冒着被骂的风险,还是在指点楼主两句。“成很多引用留在栈里面 说的正确”的理解还是错的。告诉你一个结果,第一段代码,哪怕你循环100万次,你需要的内存和循环1次是一样多的,栈中也只有一个引用。 再给你一点为学的指点:很多时候,你说明白了,你还真傻也不明白,如果你知道你其实不明白,可能比你认为自己明白更明白,然后你才有可能明白,否则,你以为明白了,永远也不会明白,却一直以为自己明白,其实不明不白。而要想明白,需要谦虚一些,低调一些,不要不懂装懂,或者看点皮毛,就迫不及待的觉得发现了真理。慢慢的,或许你会明白,不过,如果你还是不明白,那就慢慢的去明白吧。 ----------------------------------------------------- 很敬业... 足见肺腑之言哦 接受~~ |
|
返回顶楼 | |