JVM中内存划分,一个对象的创建过程,对象的访问方式
《深入理解java虚拟机》第二章2.1-2.3
一、JVM内存区域
java虚拟机中内存主要分为:
(1)程序计数器
(2)虚拟机栈(和本地方法栈)
(3)堆
(4)方法区(包括常量池)等;
首先需要理解两个概念:
线程共享内存:所有线程共享的存储区域,生命周期同虚拟机。堆、方法区线程共享。
线程私有内存:每一个线程有着独立的存储区域,生命周期同线程。程序计数器、栈线程私有,
1、程序计数器
1.1 作用:当前线程所执行的字节码的行号指示器。
1.2 特点:线程私有
1.3 如果线程正在执行的是一个java方法,则计数器值为字节码指令的地址;
如果正在执行的是本地方法,则计数器值为0;(本地方法是指一个java调用非java代码的接口)
1.4 唯一一个没有规定任何OutOfMemoryError的区域
2、虚拟机栈
1作用
它描述的是java方法执行的线程内存模型;
每一个线程对应一系列的方法,每一个方法都用一个栈帧来描述;
每一个栈帧主要包括四个内容:局部变量表、操作数栈、动态连接、方法出口。
每一个方法的生命周期就是一个栈帧从入栈到出栈的过程。
在局部变量表中,以变量槽作为存储单位(32bit)
一个方法中存放顺序为:this、参数、局部变量。this是指当前对象实例的引用,放在索引0位置,后面从1开始存放
2.1.1 局部变量表
(1)八大基本数据类型
(2)对象引用,reference类型,它可能是执行对象起始地址的引用指针,也有可能是一个句柄。具体见后面
(3)returnAddress
2.1.2 操作数栈:存储方法内一些进行了运算操作后的结果
2.1.3 动态连接:在方法内调用接口,通过字面量链接到具体的实现类,实现Java的动态特性
2.1.3 方法出口:返回地址
2.2 线程私有
2.3 两类异常
2.3.1 线程请求的栈深度大于虚拟机允许深度,stackOverflowError
2.3.2 栈扩展时无法申请到足够内存,OutOfMemoryError
2.4 本地方法栈:与虚拟机栈类似,但它为本地方法服务,在HotSpot中两者合并。
3、堆
3.1 作用:存放对象实例,为所有对象实例包括数组分配空间。
3.2 线程共享。为了更好地回收内存或更快的分配内存,可以将堆细分成几个线程私有的区域。
3.3 垃圾收集器的内存区域
3.4 逻辑上连续,但物理上可以不连续;
3.5 当堆无法扩展时,OutOfMemoryError
4、方法区
4.1 作用:存储已被虚拟机加载的类型信息、final常量、static静态变量、即使编译器编译后的代码缓存等数据;可以理解为堆的一个逻辑部分,别名“非堆”
4.2 线程共享
4.3 方法区扩展时无法申请到足够内存,OutOfMemoryError
4.4 运行时常量池是方法区的一部分。(待补充)
二、对象的创建过程
1、类加载检查
2、为对象分配内存(指针碰撞、空闲列表)
3、解决线程安全(CAS、TLAB)
4、初始化
5、对对象进行必要设置
6、构造函数
三、对象在堆中的布局
对象可以在堆中的布局分为三部分:对象头、实例数据、对齐填充;实例数据和对齐填充很好理解,主要介绍对象头的几个部分:(注意,数组对象和普通对象有区别)
(1)Mark Word:用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
(2)类型指针。大小为1字,指向方法区中的类数据。
(3)数组长度。这部分只有数组对象有。
参考https://www.cnblogs.com/maxigang/p/9040088.html
四、对象的访问定位
栈中局部变量表中的reference是指向对象的引用,而具体的定位方式有两种:
(1)句柄:堆会划分一块句柄池存储对象的句柄,reference指向的是对象的句柄,句柄中存储着两个指针,一个指向实例数据,一个指向类型数据。(二级指针)
(2)直接指针:refere直接指向对象,而类型数据由对象考虑;(一级指针)
两者各有优缺点,前者优势是reference稳定,在对象移动如垃圾回收时只需改变句柄中的实例数据指针;后者优势在于访问速度快。HotSpo主要使用第二种方式。