JVM - Java的内存区域 & 对象布局


虚拟机内存的各个部分

如上图所示,JVM运行时内存可以分为堆,栈,非堆。

栈是线程私有的,里面主要包括了程序计数器(Program Counter), 虚拟机栈(VM Stack)和本地方法栈(Native Method Stack)。

  • 程序计数器
    可以看做是当前线程执行字节码的行号指示器,程序的分支,循环,跳跃,异常处理,以及线程切换后恢复到正确执行位置都依赖此计数器完成。
  • 虚拟机栈
    每个方法在被执行的时候,虚拟机都会创建一个栈帧(Stack Frame)。一个方法被调用的过程就是一个栈帧在虚拟机栈中入栈到出栈的过程。栈帧里保存的就是局部变量表,操作数栈,动态连接,方法出口等信息。
  • 本地方法栈
    本地方法栈和虚拟机栈的功能作用相似,只是虚拟机栈是为了虚拟机执行Java字节码服务;本地方法栈是为了虚拟机调用本地方法服务。

堆,就是我们垃圾回收的主要部分,是线程共享的空间。Java中所有的实例对象都是在堆上进行分配空间的。按照一般的分代理论,可以分为新生代(Young Gen)和老年代(Old Gen)。对新生代的GC被叫做Minor GC, 对老年代的GC叫做Major GC,整个堆的GC叫做Full GC。

非堆

除了堆栈,还有一部分非堆的部分,叫做方法区,以前通过永久代来实现方法区。这部分空间也是线程共享的,主要包括了运行时常量池部分。

对象的内存布局

我们知道堆是用来给对象分配空间的,而每个对象在堆上的内存结构是怎样的呢?一般是分为三个部分:对象头(Header), 实例数据(Instance Data)和对齐填充(Padding)。

对象头

对象头分为三部分,一个是Mark Word,用来存储对象自身运行时的数据,如哈希码,GC分代年龄等等;一个是类型指针,指向类型元数据的指针;一个是数组长度,如果该对象是一个数组的情况下。对象头是一个动态数据结构,在未锁定和锁定状态下的大小都是不一样的,

Mark Word在我们说并发,synchronized, 锁升级的时候都有涉及

实例数据

存储对象真正有意义的有效信息

对齐填充

为了使得对象大小都是8字节的整数倍而存在。