java虚拟机_JVM运行时数据区
JVM运行时内存
JVM运行时数据区(JVM Runtime Area)其实就是指JVM在运行期间,其对计算机内存空间的划分和分配。
本文图片全部取自 java虚拟机原理图解
目录:
JVM运行时数据区里有什么?
补充:
-
程序计数器:当前线程所执行的字节码的行号指示器
- 字节码解释器的工作就是通过改变这个计数器的值来选取吓一跳需要执行的字节码指令。
- 分支、循环、跳转、异常处理、线程恢复都需要依赖这个计数器来完成。
-
堆:垃圾回收主要区域又叫GC堆,可固定大小,也可以是可扩展的。
- 从垃圾回收的角度看若采用分代算法可分为:
新生代
和老年代
;再细致一点就是Eden
,From Survivor
,To Survivor
; - 线程共享的java堆可能划分出多个线程私有的分配缓冲区(TLAB);
- 如果堆中没有内存可以分配,且堆无法再扩展,抛出 OutOfMemoryError
- 从垃圾回收的角度看若采用分代算法可分为:
- 方法区:又叫永久带(也会进行垃圾回收),包含运行时常量池。
- 直接内存: javaNIO的 DirectByteBuffer 可以直接引用堆外内存。不受虚拟机大小限制。
虚拟机栈是什么?虚拟机栈里有什么?
栈帧是什么?栈帧里有什么?
方法区是什么?方法区里有什么?
对象的创建
1. 空间分配
虚拟机遇到new指令时,首先检查这个指令的参数能否在常量池中定位到一个类的符号引用。并且检查这个类是否已经被加载解析初始化过,如果没有那必须先执行类的加载过程。
类加载检查通过以后,虚拟机为新生对象分配内存,所需对象大小在类加载完成以后便可以确定。把一块内存从java堆中划分出来:
-
指针碰撞(Bump the Pointer): 假设堆是绝对规整的用过的内存在一遍没用过在另一边,中间以指针为界,分配空间就是将指针向空闲区移动固定的大小;
JVM使用Serial(连续),ParNew(新标准)等带Compact(压缩整理)过程的收集器时,系统采用分配算法就是指针碰撞; -
空闲列表(Free List): java内存并不规整,已使用的和未使用的相互交错,虚拟机维护一个列表记录可用的内存,分配的时候从列表中找到一块足够大的空间划分给对象,并更新空闲列表。
采用CMS这种Mark-Sweep(标记扫描)算法的收集器时,通常采用空闲列表;
如何保证线程安全,为对象分配空间在并发情况下并不是线程安全的。例如在给A分配指针,指针还没有修改,B有使用原来的指针来分配空间。解决方案有2种:
- 对分配空间的动作进行同步处理;
- 把内存分配的动作按照线程划分在不同的空间中进行。每个线程在java堆中预先分配一小块空间称为本地线程分配缓冲(Thread Local Allocation Buffer TLAB);线程只在自己的Buffer上分配内存,只有TLAB用完需要分配TLAB的时候才需要同步锁定;
分配完成以后需要将内存空间全部初始化为0(不包含对象头);
2. 对象的内存布局
-
对象头(Header)
- 储存对象自身的运行时数据(哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程id、偏向时间戳),这部分信息在32和64位虚拟机中分别占位32bit和64bit;会有复用的情况,对象未被锁定的时候 25bit存储哈希码,4bit存储对象分代年龄,2bit存储锁标志,1bit固定为0;
- 类型指针,确定是那个类的实例;
- 如果是数组类型需要记录数组的长度信息;
- 实例数据(Instance Data) 对象中字段的值,默认同类型的数据会存储在一起(long/doubles、ints、shorts/chars、bytes、booleans、oops(Ordinary Object Pointers))
-
对齐填充(Padding)
没有特殊含义,起占位符的作用,HotSpot VM的自动内存管理要求对象的起始地址必须是8字节的整数倍,不满的话需要填充补全
2bit存储锁标志的状态
存储内容 | 标志位 | 状态 |
---|---|---|
对象哈希码,对象分代年龄 | 01 | 未锁定 |
指向锁记录的指针 | 00 | 轻量级锁定 |
指向重量级锁记录的指针 | 10 | 膨胀(重量级锁定) |
空不需要记录信息 | 11 | GC标记 |
偏向线程id、偏向时间戳、对象分代年龄 | 01 | 可偏向 |
3.对象的访问定位
对象的访问方式有:句柄和直接指针 两种,实际采用哪种方式看虚拟机的具体实现;
- java堆中画出一部分内存为句柄池,引用中存储的是句柄地址,句柄中保存真正的对象地址,当对象移动时(垃圾回收时非常频繁),只需要修改句柄中的地址,引用本身不需要修改,且句柄中保存对象类型的指针,实例中就不需要保存这部分信息了;缺点是每次引用都多了一次指针引用的开销;
- 直接保存对象的地址,速度快;