前言
在成功加载了类的class对象之后,就可以进行内存分配以及运算,这其中运算的过程中就涉及到了运行时的内存管理
一、内存分配
问题
1. 划分内存的方法
划分内存有两种方法指针碰和空闲列表,其中指针碰撞是默认的方法。
指针碰撞指的是假设Java的内存为一个整体,而其中存在一个指针使得已使用和未使用两部分明确的位于指针的两边,那么划分时只需要把指针往未使用部分后移和新对象长度相等的距离即可创建成功。
空闲列表指的是假设已使用和未使用两部分相互交错,那么虚拟机就必须维护一个列表,上面记录着未使用空间,并且在内存划分时找到一个足够大的空间给新对象,然后更新列表。
2. 划分内存的位置
位置主要是根据条件判断是栈、堆上的老年代、堆上的新生代中的eden区
3. 划分过程的并发问题
CAS/TLAB
流程图
关键点
- 采用逃逸分析判断是否放在栈空间中,并且放在栈空间中时还涉及到标量替换的概念
- 判断是否是大对象的参考参数(-XX:PretenureSizeThreshold)的设置值
- TLAB的全称是Thread Local Allocation Buffer(线程本地分配缓存区),即每个线程都有一块比较小的缓冲区,假如对象小于这块空间就可以直接分配成功,而只有大于缓冲区时才会去eden区创建这时才有可能和其他线程发生并发问题
二、内存划分
分区图
举例
public class Car {
public int run(int oil) {
int speed = 5;
int range = oil * speed;
return range;
}
public static void main(String[] args) {
Car car = new Car();
int oil = 10;
int range = car.run(oil);
System.out.println(range);
}
}
首先整个main方法是程序的入口,所以先将main()方法所在的类Car进行类加载,又因为不包含静态代码块所以不用执行,接着开始执行main方法,其中第一句需要new一个对象。
创建对象先进行类加载检查可以发现Car类已经被加载过了,开始进行内存划分的逃逸分析、大对象、TLAB三重判断然后得到一块内存,得到内存时首先将内存块重置为0值,然后进行对象头设置以及init方法执行完成对象创建。
对象创建完成就会调用run()方法,首先是在栈上开辟一个块新的栈帧,然后其中的局部变量表就是用来存放speed这种局部的,而操作数栈就是用来辅助range的计算过程,因为这一句Java代码对应的指令是多条,所以过程中需要辅助空间,动态链接主要存放的是一些引用值,在运行时才会通过查找得到直接链接,方法出口即记录了正常或异常的返回地址。
程序的运行主要依靠的就是字节码执行引擎将字节码再进一步转化为机器指令,操作cpu进行运算,而程序计数器就是线程独有的用来记录程序字节码执行位置的变量。
执行完主体逻辑之后,JVM就会关闭,终止进程。