zoukankan      html  css  js  c++  java
  • Java之深入JVM(3) 由一个栈溢出的问题看Java类和对象的初始化 (转)

    今天,在一个群里面有网友问到这样一个问题,以下代码被调用运行时为何会造成栈溢出(StackOverflowError)的错误:

    public class Constructor {
        Constructor c = new Constructor();
    
        public static void main(String[] args) {
            Constructor test = new Constructor();
    
        }
    }
    一般人,初看感觉没啥问题,但是自己在机器上跑了一下,就会爆出这样的错误,如图
    stackoverflowerror 
     
    从这些错误中我们可以得到这样一个信息:程序运行时候,Constructor实例初始化方法
    (在这里就是<init>,这个后面还会细讲),被疯狂的调用。
     
    群里面的人,对这个问题的回答,其中有个网友通过现象推结果,说是:“在这个Constuctor类中,由于类的成员c本身就是
    Constructor类型的,所以当类的成员初始化时,类的构造函数就被递归调用了”。 
     
    这个回答,说实话挺没逻辑的,看的我比较云里雾里。其实这个问题如果我们从反汇编后的该类的字节码入手,
    就能很清楚的得到问题的答案了.
    我们用java –p Constructor 得到反汇编后的字节码,如下:
    复制代码
    public class Constructor extends java.lang.Object{
    Constructor c;

    public Constructor();
      Code:
       
    0:   aload_0
       
    1:   invokespecial   #10//Method java/lang/Object."<init>":()V
       4:   aload_0
       
    5:   new     #1//class Constructor
       8:   dup
       
    9:   invokespecial   #12//Method "<init>":()V
       12:  putfield        #13//Field c:LConstructor;
       15:  return

    public static void main(java.lang.String[]);
      Code:
       
    0:   new     #1//class Constructor
       3:   dup
       
    4:   invokespecial   #12//Method "<init>":()V
       7:   astore_1
       
    8:   return

    }
    复制代码
     
     
       我们只要关注此类的构造方法Constructor中的代码就行了,我们可以发现在这构造方法里面,
    出现了new #1;//class Constructor 这样的语句,他表示创建一个Constructor类型的对象。
     
    从这里面我们便可以明白:
    即便你在构造函数外面,显式的初始化了一个成员如c,但是类编译后运行时,
    这种显式初始化成员的真正初始化还是放在构造函数中,统一进行的。
     
    所以像刚才的那种代码,相当于就是在Constructor构造函数里面调用了自身.就像下面代码一样:
    复制代码
    public class Constructor {

        Constructor c;

        
    public Constructor() {
            c 
    = new Constructor();
        }

        
    public static void main(String[] args) {
            Constructor test 
    = new Constructor();

        }
    }
    复制代码
     
    你说怎么可能不栈溢出呢?
    PS:顺便补充一下,几条Bytecode指令的意思: new 创建一个新对象.
           invokespecial  根据编译时类型来调用实例方法.
           invokevirtual   根据运行时对象实际类型,来调用实例方法.
           putfield          设置对象中字段的值.  
     
     
  • 相关阅读:
    PAT B1045 快速排序 (25 分)
    PAT B1042 字符统计 (20 分)
    PAT B1040 有几个PAT (25 分)
    PAT B1035 插入与归并 (25 分)
    PAT B1034 有理数四则运算 (20 分)
    PAT B1033 旧键盘打字 (20 分)
    HDU 1231 最大连续子序列
    HDU 1166 敌兵布阵
    HDU 1715 大菲波数
    HDU 1016 Prime Ring Problem
  • 原文地址:https://www.cnblogs.com/royi123/p/3132086.html
Copyright © 2011-2022 走看看