Java之深入JVM(3)
来源:互联网 发布:汇量科技 知乎 编辑:程序博客网 时间:2024/05/17 21:07
今天,在一个群里面有网友问到这样一个问题,以下代码被调用运行时为何会造成栈溢出(StackOverflowError)的错误:
public class Constructor { Constructor c = new Constructor(); public static void main(String[] args) { Constructor test = new Constructor(); }}
一般人,初看感觉没啥问题,但是自己在机器上跑了一下,就会爆出这样的错误,如图
从这些错误中我们可以得到这样一个信息:程序运行时候,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 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();
}
}
Constructor c;
public Constructor() {
c = new Constructor();
}
public static void main(String[] args) {
Constructor test = new Constructor();
}
}
你说怎么可能不栈溢出呢?
PS:顺便补充一下,几条Bytecode指令的意思: new 创建一个新对象.
invokespecial 根据编译时类型来调用实例方法.
invokevirtual 根据运行时对象实际类型,来调用实例方法.
putfield 设置对象中字段的值.
阅读全文
0 0
- Java之深入JVM(3)
- Java之深入JVM(6)
- Java之深入JVM(1)
- 深入了解Java之四(JVM)
- 深入JVM之Java内存模型
- 深入JVM之Java对象访问模式
- 深入JVM之Java引用类型
- 4.1 jvm 深入学习之 java 引用
- 深入理解Java之jvm启动流程
- 深入理解JVM之走进Java
- 深入理解JVM 第一章 之3
- 深入JVM之二
- 深入Java,初探JVM
- 深入Java,初探JVM
- 《深入Java虚拟机》导读之四: JVM体系结构
- 《深入Java虚拟机》导读之四: JVM体系结构
- 深入理解JVM之java代码的执行机制
- 深入理解java虚拟机之JVM调优配置
- js生成二维码
- tomcat部署升级
- C#通过WIN32 API 获取外部程序sysListview的值和TreeView的值
- mysql between and 和in效率问题
- C++实训2-2
- Java之深入JVM(3)
- (Ext / Js) ajax 跨域请求发送两次解决方案
- Unity Shader 学习笔记(9) 渐变纹理、遮罩纹理
- android 执行monkey指令的方法
- 定义一个圆形类,实现圆的的面积计算
- api设计干货
- (二)Git的安装与配置
- centos-搭建https服务器
- Linux基本指令、文件管理权限及Linux重要目录的整理