运行时数据区
程序计数器
- 程序计数器可以看做是当前线程所执行的字节码的行号指示器
- 每条线程都有一个独立的程序计数器,属于线程私有内存
- 此内存区域是唯一一个没有规定OOM情况的区域
Java虚拟机栈
- 线程私有,生命周期与线程相同
- 虚拟机栈描述的是java方法执行的内存模型.每个方法从调用到执行完成的过程.都对应这一个栈帧在虚拟机栈中的入栈和出栈
- 栈帧是方法运行时的基础数据结构
- 局部变量表:存放编译期可知的基本数据类型,对象引用,returnAddress类型,
- 虚拟机执行Java方法服务
本地方法栈
Java堆
- 线程共享
- 垃圾回收器管理的主要区域,也被成为GC堆
- 从分代收集算法的内存回收角度来划分
新生代 包含:eden空间/from servivor空间 / to survivor空间
老年代
方法区
- 线程共享
- 存储已被虚拟机加载到类信息,常量,静态变量,即时编译器编译后的代码
运行时常量池
- 线程共享
- 存储已被虚拟机加载到类信息,常量,静态变量,即时编译器编译后的代码
运行时常量池
- 运行时常量池是方法区的一部分
- 存放编译期生成的个中字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放.
直接内存
hotspot虚拟机对象探秘
对象的创建
分配内存
类加载完成后便完全确定了对象所需的内存大小
指针碰撞
假设java堆中的内存都是绝对规整的,所有用过的内存都放在一边,空闲的内存放到另一边.中间放着一个指针作为分界点的指示器.那所分配的内存仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离.
空闲列表
Java堆不规整,已使用的内存和空闲的内存相互交错.虚拟机必须维护一个列表.记录哪块内存是可用的.在分配的时候从列表中找到一块足够大的的空间划分给对象,并更新列表上的记录
CMS收集器方式
采用CMS收集器的 通常采用空闲列表
分配内存的并发问题
CAS
采用CAS配上失败重试的方式保证原子性
TLAB
每个线程预先在Java堆中分配一块内存称为本地线程分配缓冲(TLAB),哪个线程分配内存,就在哪个线程的TLAB上进行分配. 设定: -XX:+/-UserTLAB
设置对象信息
将信息存放到对象的对象头
init
从虚拟机的角度来看,对象已经创建完成.new 指令完成 .但从java角度来看,对象创建刚刚开始,init()方法还没有执行.所有的字段都为零. 执行new指令后接着执行init指令进行初始化,这样一个对象才算完全产生.
对象的内存布局
对象头
- 第一部分(mark word) 存储对象自身运行时数据(哈希吗.GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID),
- 第二部分类型指针,即对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例.
实例数据
对象真正存储的有效信息.即程序代码中定义的各种类型的字段内容
对齐填充
保证对象的大小必须是8字节的整数倍
对象的访问定位
-
句柄
-
直接指针
实战:OOM异常
略.