深入理解JVM阅读笔记(一)
1.JVM内存区域划分
线程私有
- 程序计数器:该计数器记录的时是正在执行的虚拟机字节码指令的地址,如果正在执行Native方法,该计数器则为空。字节码解释器通过修改该计数器的值来获得下一条需要执行的字节码指令。
- 虚拟机栈:虚拟机栈是描述方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧,栈帧里存储了局部变量表,操作数栈,动态链接和方法出口信息。方法从开始执行到执行完成,就对应一个栈帧从入栈到出栈的过程。会抛出两种异常,StackOverflowError(线程请求的栈深度大于虚拟机允许的深度)和OutOfMemoryError(扩展后无法获得足够的内存)。
- 本地方法栈:虚拟机栈执行java方法,本地方法栈执行Native方法,会抛出两种异常,StackOverflowError和OutOfMemoryError。
线程共享
- 堆:Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。并且Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆,会抛出OutOfMemoryError。
- 方法区:称为“非堆”,用于存储已被虚拟机加载的类信息,常量,静态变量和即时编译器编译得代码等数据。在Hotspot中,它在GC算法中被定义为为永久代,和Java堆一样不需要连续的内存和可以选择固定大小或者可扩展,会抛出OutOfMemoryError。
- 运行时常量池:Class文件中包含有一项信息,常量池,用于存放编译期间生成的各种字面量和符号引用,这部分在类加载后放入方法区的运行常量池中存放。具有动态性,常量不一定是编译期产生的,也可以是运行期产生。
直接内存:由于NIO包的引入,引入了一种基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用操作。会抛出OutOfMemory异常
2.对象的创建
-
类加载检查
-
为新生对象分配内存,通过两种方式分配:指针碰撞和空闲列表,使用哪种方法取决于Java堆中内存是否绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器。Java堆是否规整又取决于GC是否带有压缩整理功能。
指针碰撞:Java堆中内存绝对规整的,用一个指针作为用过的内存和空闲的内存的分界的指示器,分配内存实际上就是把指针向空闲空间挪动一段与对象相等的距离。
空闲列表:已使用的内存和空闲的内存相互交错,虚拟机必须维护一个列表,记录哪些内存块可以被使用,从列表找出足够大的空闲内存块来存放新对象,并更新列表上的记录。
-
解决同步性问题,两种方案,一种是采用CAS锁配上失败重试实现同步处理,保证原子性,另一种是给每条线程划分一个独立的内存空间,称为本地线程分配缓冲,哪个线程要创建对象,就在哪个线程的TLAB上分配内存,该方法只有当TLAB用完了,才需要同步锁定来分配新的内存。
-
虚拟机将需要分配到的内存空间都化为数据类型对应的零值。
-
设置对象头,将对象的元数据,hashcode和GC分代年龄,是否启用偏向锁等等记录进对象头。
-
执行init方法,将对象按照程序员的意愿进行初始化。
3.对象在JVM中的存储区域组成
-
对象头:由
Mark Word
,Klass Pointer
和Array Length
(可能有)组成,长度取决于系统是32bit还是64bit长度 内容 说明 32/64bits Mark Word 存储对象的hashcode和锁标记等 32/64bits Class Pointer 存储指向类的class对象的指针 32/64bits Array Length 数组的长度(若有数组则使用) - Mark Word:
> 注:当对象处于未锁定的状态时,Mark Word存储对象的哈希码和对象的分代年龄。
> 注:每个monitor对象实例和一个类的实例相互关联,随着class实例的出现而出现,消失而消失。
-
Class Pointer:该指针指向的是该类的class对象(即存储metadata的对象)
-
实例数据:存储该类的数据信息和父类的数据信息
-
对其填充:因为JVM默认要求对象的起始地址必须是8bit的倍数,因此当对象实力部分没有对齐时,需要对其填充。
注:一个空对象在JVM中占据的空间大小为8bit
4.如何通过栈上的引用定位到堆中的实例?
两种方式:句柄和直接指针
句柄访问:在堆中划分出一块区域专门存储句柄称为句柄池。栈中的reference存储的就是句柄的地址,句柄中包含了指向对象实例数据的指针和指向对象类型数据的指针。优点是:reference中存储的是句柄地址,如果对象被移动,只会改变句柄中的对象实例指针而不会改变reference本身。
直接指针:reference中存储的是对象地址,优点是速度快,省去了一次指针定位的时间开销优点是速度快,省去了一次指针定位的时间开销