【菠萝码】深入理解Java虚拟机-第二章#JAVA内存区域与内存溢出异常

262 阅读3分钟

写在前面

最近刚好在拜读 周志明 老师的 《深入理解JAVA虚拟机》,一方面为了加深自己的印象,另一方面也是分享一下书中的精华部分。所以按章节仅照自己的理解记录。谢谢

一、运行时数据区域

定义 java虚拟机在java运行时管理的数据区域

1.程序计数器(线程私有)

如果执行的是java方法,记录的是正在执行的字节码指令地址。

如果是本地方法,计数器的值为

2.Java虚拟机栈(线程私有)

虚拟机栈的生命周期和线程相同。

每一个方法被执行的时候,虚拟机都会同步创建一个栈帧。

存储:局部变量表、操作数栈、动态连接、方法出口等信息

3.本地方法栈(线程私有)

和java虚拟机栈的区别仅为这里存储的是虚拟机使用到的本地方法服务

4.Java堆(线程共享)

存放对象实例。

这里有提到 栈上分配,标量替换。后面章节会说

堆中可以划分出多个线程私有的分配缓冲区TLAB,提升对象分配时的效率

物理上不连续,逻辑上连续

5.方法区 (线程共享)

存储已经被虚拟机加载的类型信息,常量,静态变量等数据

如果方法区无法满足新的内存分配需求,会OOM,但是由于JDk1.8改为了本地内存实现的元空间。所以很难出现由于方法区导致的OOM

6.运行时常量池

方法区的一部分。

存放编译器生成的各种字面量和符号引用

7.直接内存

注意:不是虚拟机运行时数据区的一部分,也不是 虚拟机规范里 定义的内存区域。只是因为会被频繁使用,而且回导致OOM。

如果忽略掉直接内存,在配置虚拟机参数的时候,会导致各个内存区域的总和 大于物理内存的限制。导致在动态扩展的时候OOM。

二、HotSport虚拟机对象相关

1.对象的创建

创建过程: ①遇到字节码New的指令时会先去检查这个参数是否可以在常量池中定位到类的符号引用,并检查是否加载,解析,初始化过。

② 分配内存,两种方式,指针碰撞,空闲列表。具体用哪种,根据垃圾收集器来确定。关于分配内存的线程安全性问题,有两种解决方案,分别是 CAS的方式 和 TLAB的方式

③设置一些对象头的信息,比如 分代年龄,哈希码 等等

④最后一步 也就是按照程序员的意愿对对象进行初始化。这样一个真正的对象被完全构造出来。

对象的内存布局

分3块:对象头、实例数据、对齐填充

对象头包括2部分:MarkWord & 类型指针

MarkWord存储的是 哈希码、分代年龄、锁状态、锁、偏向的线程ID、时间戳等

类型指针 指向它的类型元数据的指针

对齐填充 主要目的是为了保证对象的大小都是8字节的整数倍

对象的访问定位

两种方式:句柄 和 直接指针

句柄其实就是划出一块地址作为句柄池,用来保存对象地址

直接指针 保存的就直接是对象的地址

hotspot采用的是 直接指针的方式。