【参考链接】
实例分析Java代码运行时内存布局http://blog.csdn.net/wangyy130/article/details/52100907
运行时数据区http://blog.csdn.net/u012440687/article/details/52021393
1、栈帧的顺序画反了
2、这里都是通过参数进行数据传递,主要是在操作局部变量表,如果通过对象的成员变量进行传递,则是操作堆内存
通过如下一个实例,研究一下运行时各内存区域的状态变化,主要是为了讲解,当方法调用完成后,栈内存就会自动被回收。
TestB类有一个method1()方法,接收两个参数返回他们的和
Java Code
1 |
| package com.shadowfaxghh.test; |
TestA类有一个方法method1(),会调用TestB的method1()
Java Code
1 |
| package com.shadowfaxghh.test; |
Main类作为执行入口
Java Code
1 |
| package com.shadowfaxghh.test; |
首先运行的时候要去加载Main类,在方法区生成类Main的运行时数据结构,在堆上生成其Class对象
接着运行主线程,执行其中的main()方法,会往栈中压入一个main()方法的栈帧。
因为是静态方法,所以没有隐藏的this局部变量
局部变量表中一共有3个局部变量,其中第0个局部变量作为形参其内容已经确定了。
操作数栈中目前还是空的。
接下来执行main()方法的字节码指令
先去加载TestA类
然后在堆上创建一个新的TestA对象,并将其引用压入操作数栈
然后复制一份栈顶元素,弹出栈顶元素,调用其构造函数,并压入返回值,因此时返回值为空,故没有压入
然后弹出栈顶元素,将其赋值给第1个局部变量
将第1个局部变量压入操作数栈,弹出栈顶元素,并调用其method1()方法
会往栈中压入一个method1()方法的栈帧
局部变量表中有3个局部变量,第0个局部变量为TestA对象的引用
接下来执行TestA的method1()方法
同样是加载类TestB,复制一份引用,弹出栈顶元素,并调用其构造函数,再弹出栈顶元素将其赋值给第1个局部变量
将第1个局部变量压入操作数栈
将100,200压入操作数栈
弹出栈顶元素,调用TestB对象的method1()方法
向栈中压入一个method1()方法的栈帧
局部变量表中有3个局部变量,其中第1个局部变量作为形式参数为100,第2个局部变量作为形式参数为200
接下来执行TestB的method1()方法
将第1个、第2个局部变量压入操作数栈,弹出栈顶元素,进行相加操作,并将结果压入操作数栈
TestB的method1()方法返回,将TestB的method1()的栈顶元素弹出并压入到调用方法的操作数栈,清除TestB的method1()方法的栈帧
回到TestA的method1()方法中继续执行
调用return指令,将栈顶元素弹出并压入调用方法的操作数栈,清除TestA的method1()方法的栈帧
此时已经没有引用指向堆中的TestB对象了,在进行下次堆内存垃圾回收时,会回收掉它
回到Main的main()方法中继续执行,弹出栈顶元素并赋值给第2个局部变量
然后获取静态成员变量System.out压入操作数栈,将第2个局部变量压入操作数栈,弹出栈顶元素并调用其println()方法,将返回结果压入操作数栈,没有结果故不压入
最后执行return返回
其中System的静态成员变量out,引用是在方法区的类System的运行时数据中,实例是在堆中。下图简略描述一下