1. JVM的运行时数据区划分:
JVM的运行时数据区
Java虚拟机会在执行Java程序的过程中把内存划分为上图所示的若干数据区域,这些区域有各自的用途、创建时间和销毁时间。
①程序计数器:程序计数器是当前线程所执行的字节码的行号指示器。它是线程所私有的。
②Java虚拟机栈:方法在执行的时候会创建相应的方法栈,用来保存局部变量、操作数栈、动态链接、方法出口等信息。它也是线程所私有的。
③本地方法栈:与虚拟机栈类似,不过它是native方法的栈。
④Java堆:Java堆是所有线程共享的一块区域,在虚拟机启动时创建,它的唯一目的就是保存对象实例。Java堆是垃圾收集器管理的主要区域,因此Java堆也叫作“GC堆”。
⑤方法区:方法区是各个线程共享的一块区域,它用于存储已被虚拟机加载的类信息、即时编译器编译后的代码、常量、静态变量等数据。
运行时常量池:运行时常量池是方法区的一部分,用于存放编译期生成的各种字面量和符号引用(“符号引用是什么,为什么需要符号引用”见第六章整理)。
2. Java对象的创建过程:
①先检测这个指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用所代表的类是否已被加载、解析和初始化过,如果没有,要先执行类加载过程;
②类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可完全确定;
③内存分配完成后,虚拟机需要将分配到的内存空间全部初始化为零值(不包括对象头)。
④接下来虚拟机需要对对象进行必要的设置,例如这个对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Head)中。
⑤执行<init>方法,把对象按照程序员的意愿进行初始化。
3. Java对象的内存布局:
在HotSpot虚拟机中,对象的内存布局可以分为3个区域:对象头(Object Header)、实例数据(instance data)、对齐填充(padding)。
①对象头:包括两部分信息:
I.存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳。
II.存储类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。如果对象是一个数组,那么在对象头中还有一个记录数组长度的数据。
②实例数据:对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中所定义的,都需要记录下来。
③对齐填充:并不是必然存在的,也没有特别的含义,仅仅起到占位符的作用。因为对象的起始地址必须要地址对齐,像HotSpot中要求对象的起始地址必须是8字节的整数倍,也就是所有对象的大小要是8字节的整数倍,而对象头必然是8字节的整数倍,所以当实例数据部分没有对齐时需要填充来进行对齐。
4. 对象的访问定位:
Java程序通过站上的reference来操作堆上的具体的对象。主流的对象访问方式有句柄和直接指针两种:
通过句柄访问对象
通过直接指针访问对象