JVM - 3:JVM中的内存区域,分别是用来干嘛的

回顾

上一篇讲到了

1、类加载机制
加载、验证、准备、解析、初始化、应用、卸载

2、什么情况下会加载一个类
6种主动使用方式
new
调用类的静态变量
调用类的静态方法
反射
被JVM启动时标为启动类的类
初始化一个类的子类

3、双亲委派模型
始终由自己的父类加载器去加载,父加载器加载不了的,回退给自己的子类去加载,避免了很多类重复加载的问题


JVM内存区域划分

JVM在启动我们写好的代码时
必须使用多块内存空间来存放不同的数据
然后配合我们写好的代码,才能让系统运行起来

那么加载进JVM的代码,例如对象、成员变量,静态变量,方法都放在那了呢?

1、存放类的方法区
这个区域在JDK1.8之前是叫方法区
主要用来存放 class 文件加载进来的类
还会有一些类似常量池的东西放在这个区域里
在1.8之后,改为 元空间

2、执行代码指令用的程序计数器
假设我们有如下代码
JVM - 3:JVM中的内存区域,分别是用来干嘛的_第1张图片
经过编译之后,会得到 .class 结尾的字节码文件
JVM - 3:JVM中的内存区域,分别是用来干嘛的_第2张图片
编译之后,类似于这种

所以当JVM加载类信息到内存之后,实际会使用自己的字节码执行引擎,来执行我们的指令代码

那么在执行字节码指令的时候,就需要一块内存区域,就是程序计数器

这个程序计数器是用来记录当前执行字节码指令的位置的,也就是具体执行到那一条指令

JVM是支持多线程的,所以我们写好的代码,可能会开启多个线程来执行不同的代码,也就是有多个线程来执行不同的代码指令

因此,每个线程都有一个程序计数器,专门记录当前这个线程执行到那一条字节码指令了。也就是说程序计数器,是当前线程私有的。

3、虚拟机栈

Java代码在执行的时候,一定是执行某个方法中的代码

在 main 线程执行 main() 方法的时候,就会通过 main 线程对应的程序计数器,来记录自己执行指令的位置

通常,在这个方法中,我们会定义一些局部变量

因此、JVM中必须有一块区域是用来保存每个方法内部的局部变量等数据的,这个区域就是虚拟机栈。

每个线程都有自己的虚拟机栈,也就是说虚拟机栈,是当前线程私有的

如果,线程执行了一个方法,就会对这个方法创建一个对应的虚拟机栈

这个栈帧中,就存有这个方法的局部变量表,操作数栈、动态链接,方法出口等东西

如果一个方法中,调用了另外一个对象的方法怎么办呢

会为另外一个方法创建一个栈帧,然后把这个栈帧压入线程自己的虚拟机栈中去

然后自己的局部变量表,就会有 被调用方法的局部变量

如果,这个被调用方法执行完毕,就会让这个栈帧从线程的虚拟机栈中出栈

4、堆内存
堆内存,是用来存放,我们 new 出来的各种对象的
JVM - 3:JVM中的内存区域,分别是用来干嘛的_第3张图片
在线程执行 main 方法的时候,会在 main 的线程栈 的局部变量表中,让一个引用类型 局部变量存放 这个实例对象的地址(对象的堆内存地址)

5、其它内存区域
在JDK很多底层API中,比如 IO , NIO ,网络 Socket相关的
源码有很多是 native 代码
这种是调用本地操作系统里面的一些方法,可能调用的是C语音写的方法。或者一些底层类库
那么这种,就会有一个线程对应的本地方法栈,也是属于线程私有的

6、流程总结
JVM 会先加载 class 文件到内存里
然后会调用 main 方法
这个 main 线程会创建一个 程序计数器,属于当前线程私有的,用于记录代码执行到那一行指令
这个 main 线程会创建一个 方法栈。属于当前线程私有的,用于记录当前方法所需的局部变量,操作数栈,动态链接,方法出口等等信息
会把这个 main 方法压入 线程栈中
如果遇到创建对象 ,这个时候,会启动类加载机制,把对应的类加载进内存,进行 加载、验证、准备、解析 等等过程。在堆内存开辟一片内存空间。把这个对象的堆内存地址,赋予 main 方法中 调用和这个对象的 变量
如果调用这个对象的某个方法,会为这个方法创建一个栈帧,压入 main 线程的 虚拟机栈中,执行完毕之后,出栈。

你可能感兴趣的:(JVM,JVM,JVM)