之前有篇文章写了Java虚拟机的内存管理
本篇文章详细写运行时数据区域。
程序计数器: 记录当前运行所在的行数。
为什么要设计程序计数器?
我们知道,CPU是根据时间片来进行调度的。当时间到达之后,CPU资源就给了其他线程。但是这个时候我们线程还没执行完毕。那后面再次获取时间片的时候我们要如何恢复线程呢?就是通过程序计数器。恢复到上次执行的哪一行。
虚拟机栈: 是线程独有的。我们肯定通过栈帧,这个说法。每个线程有自己的栈,先进后出。
看示例程序一。我们的线程先调用main方法,然后调用funB方法,funB在调用funA。栈里面存的什么?栈帧是针对方法来说:先main方法入栈,再funB入栈。再funA入栈。
funA里面过程:
- 5 6 入操作数栈,进行相加等于11 将11出栈赋值给局部变量C(局部变量表)
- 将11入栈,5入栈 进行相乘,将结果55出栈,赋值给局部变量C
- 将C返回。方法出口记录的 funB的int c = funA(a,b)所在的行
知道过程。对栈里面存储什么就应该清楚了
存储:局部变量表(基础数据类型,引用类型)。操作数栈(所有的操作都是在这里完成。最后一定是空的)。动态链接(有时候,编译完成是不知道引用的是哪个类的(多态)。记录符号引用)。方法出口(记录方法执行完毕后,下一个指令的行号)
// 示例程序1
public class JvmTest {
private int temp;
private int funA(int a, int b){
int c = a + b;
c = c * 5;
return c;
}
private void funB(){
int a = 5;
int b = 6;
int c = funA(a, b);
System.out.println(c);
}
public static void main(String[] args) {
JvmTest jvmTest = new JvmTest();
jvmTest.funB();
}
}
本地方法栈 过程同虚拟机栈,只不过虚拟机栈是执行Java方法。本地方法栈执行的是C方法(因为很多库函数是C写的。之前也没得java)
方法区 我之前因为方法区是永久代。但是我后面又看了深入理解Java虚拟机第三版。方法区JDK1.8之后不在永久代。在元空间里面。就是直接内存。 里面存储类信息(为什么反射能够在运行时找到类的所有信息。因为就存在方法区里)。现在应该能够理解方法区存什么了吗。存储类信息:例如方法,静态变量,运行时常量。以及其他信息
堆: 存储具体的实例。对象头(锁信息,分代年龄,其他)+ 实例数据(例如 示例程序里面,实例jvmTest里面其实就能存储了 temp=0)+ 占位符
需要注意的是:实例存在在堆里面。栈里面是有些对象变量。或常量,那个是存储的对象的地址。就是一个指向堆里某个位置的引用。
现在将我的示例程序运行过程描述一遍
- 程序启动,先加载类。将类信息全部加载到方法区(类有哪些变量,有什么方法) 【方法区-线程共享】
- 启动线程运行。先将main方法入栈。 【虚拟机栈-线程独有】
- 创建一个jvmTest对象:在堆新建一个jvm对象:包含对象头,实现信息temp=0,填充数据 【堆-线程贡献】
- 调用funB,将funB入栈。【虚拟机栈】
- 加载局部变量(a,b)到局部变量表 【虚拟机栈】
- 调用funA,将funA入入栈。 加载局部变量,并进行运行。【虚拟机栈】
- 计算出C并返回后,funA出栈【虚拟机栈】
- 打印C的值后,funB()出栈。【虚拟机栈】
- main方法结束,main出栈线程结束【线程死亡,全部已经出栈,这块内存已经释放】
10.垃圾回收运行,回收jvmTest对象【释放堆空间】
有没有留意到:栈空间释放了,堆空间也释放了。程序计数器(线程里面,记录行数的,没运行一步,其值就会改变,线程没了)也释放了。但是方法区空间好像没有释放?
在深入理解java虚拟机里面,作者有说:方法区条件释放很苛刻。下面3者缺一不可:
①所有该对象的实例已经被回收。包括其子类的对象
②给类的类加载器已经被回收
③该类的Class对象也已经被回收
路漫漫其修远兮,吾将上下而求索 博主:五更依旧朝花落