运行时数据区域
程序计数器
- 当前字节码的行号指示器
- 线程私有
栈区
- 线程私有
Java虚拟机栈
- 为虚拟机执行Java方法(即字节码)服务
- 存储局部变量表、操作数栈、动态链接、方法出口等
- 局部变量表:编译期间完成分配
存储基本数据类型、对象引用
本地方法栈
- 为执行Native方法服务
- HotSpot将本地方法栈和虚拟机栈合一
Java堆
- 线程共享
- 目的:存放对象实例
- 垃圾回收器管理的主要区域
- 通过-Xmx和-Xms动态扩展
方法区
- 虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等
- 内存回收目标主要是针对常量池的回收和对类型的卸载(类型卸载较负载但必要)
- 运行时常量
- 存放编译期生成的各种字面量和符号引用
- 具有动态性:运行期间可以放入新的变量
直接内存
- NIO类,可使用Native函数库直接分配堆外内存,通过存储在堆中的DirectByteBuffer对象作为这块内存的引用进行操作
HotSpot虚拟机对象探秘
对象的创建
- 创建新对象(new 指令),先检查指令的参数能否在常量池中定位到类的符号引用,并检查是否被加载。如果没有,需进行类加载过程
- 内存分配:
- 为新生对象分配内存:
- 内存规整:指针碰撞
- 内存不规整:空闲列表
- 并发情况下的线程安全:
- 对分配内存空间的动作进行同步处理,保证原子性
- 每个线程在Java堆中预先分配一小块内存,即本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)。在每个线程自己的TLAB上分配内存,只有分配新的TLAB时,才需要同步锁定
- 为新生对象分配内存:
- 对象的初始化和设置:
- 将分配的内存空间初始化为零值(使用TLAB可以在分配TLAB时初始化)
- 设置对象头
<init>
方法,将对象按照程序员的意愿初始化
对象的内存布局
- 对象头Header:存储对象自身的运行时数据
- Mark Word:HashCode、GC分代年龄、锁状态标志、线程只有的锁、偏向线程ID、偏向时间戳等
- 类型指针:指向类元数据的指针,用于确定对象是哪个类的实例(非必要)
- 对齐填充(非必要):占位符,因为对象必须是8字节的整数倍
对象的访问定位
需要通过栈上的reference数据操作堆上的具体对象
- 句柄访问:
- 在堆中划分一块内存作为句柄池,reference中存储对象的句柄地址,句柄中包含对象实例数据和类型数据各自的具体地址信息
- 优点:稳定的句柄,在对象移动(如gc操作)时,不需要改变reference
- 直接指针访问:
- 堆对象的布局中需要考虑如何放置类型数据,reference中存储的是对象实例数据地址
- 优点:速度快,节省了指针定位的时间开销(HotSpot)