最近看了《深入理解Java虚拟机》,把其中重要的知识点记录下来。
1、运行时数据区
在Java虚拟机中有下面几个运行时数据区:虚拟机栈、本地方法栈、程序计数器、方法区、堆。
1.1程序计数器
当前线程执行字节码的行号指示器。Java虚拟机的多线程是通过线程轮流切换获取内核资源执行的,对于同一个内核,每个时间都只有一个线程在那里执行。程序计数器的作用是让线程在切换后能回到之前的执行位置上。
1.2Java虚拟机栈
虚拟机栈的生命周期和线程相同。每个方法在执行时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译器可知的各种基本数据类型、对象引用(reference类型,可能指向对象地址的引用指针,也可能指向代表对象的句柄)。
int a = 12; Person b = new Person();
上面这个代码,其中a是变量,12是a这个变量指向的数据。如果是基础类型,那么变量和数据的内存是在同一个地方,都在虚拟机栈或方法区或堆中,而如果是引用类型,比如变量b会和对应数据new Person()的内存地址或句柄地址放在一起,new Person()的实例数据会放在堆中。
1.3本地方法栈
和虚拟机栈发挥作用类似,不过本地方法栈为Native方法服务,有的虚拟机会把虚拟机栈和本地方法栈合二为一,比如HotSpot虚拟机。
1.4Java堆
存放对象实例的地方,是垃圾收集器管理的主要区域。
1.5方法区
存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。其中运行时常量池就是放在方法区中。
1.6直接内存
直接内存不是虚拟机运行时数据区的一部分,JDk1.4新加入了NIO类,可以使用Native函数库直接分配堆外内存。
2、对象的内存布局
在HotSpot虚拟机中,对象在内存中的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。
2.1对象头
对象头包括2部分信息:
(1)Mark Word:存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
(2)类型指针:对象指向方法区中它的类元数据的指针,虚拟机通过这个来确定这个对象是哪个类的实例。
(3)如果对象是一个Java数组,在对象中还有一块用于记录数组长度的数据。
2.2实例数据
用于存储对象实例的各种字段内容,自身类的和父类继承的都会记录下来。
2.3对齐填充
对象的大小必须是8字节的整数倍,如果不是整数倍就使用对齐填充补全。
2.4对象的访问对位
Java程序通过虚拟机栈上的对象引用(reference类型)来操作堆上的具体对象,对象访问方式取决于虚拟机实现,主流的访问方式有使用句柄和直接指针2种。
(1)使用句柄访问:Java堆中划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据地址信息和类型数据的地址信息。
(2)使用直接指针访问:reference存储的是对象地址,即对象类型数据的地址信息和实例数据。HotSpot虚拟机使用的是直接指针访问方式。
使用句柄访问最大好处是在对象被移动时不会改变对象引用,只是改变句柄的实例数据指针;使用直接指针的最大好处是速度快,节省了一次指针定位的时间开销。