Java虚拟机管理的内存的区域可由下面这张图表示
这些内存区域可以划分为线程私有和线程共享两类:
- 线程私有即是指该内存区域与线程同生共死,且该区域仅对该线程可见.
- 线程共享则是指该内存区域随虚拟机启动而开辟,随虚拟机关闭而释放,且该区域对所有线程可见.
线程私有
- 程序计数器:
一块小的内存区域,是当前线程所执行的字节码的行号指示器.
当实行java方法时,值为虚拟机字节码指令地址;执行native方法时,值为空. -
虚拟机栈:
从对应关系来看,一个线程对应一个虚拟机栈,而线程的一个方法对应虚拟机栈中的一个栈帧.可表现为下图:
一个栈帧中储存了一个方法的局部变量表,操作数栈,动态链接,方法出口等.
线程调用一个方法即是一个栈帧入栈,结束一个方法即是一个栈帧出栈. - 本地方法栈:
与虚拟机栈的结构,作用类似,不同的是虚拟机栈执行的是java方法,本地方法栈执行的是native方法.
注:有的虚拟机将二者合二为一.
线程共享
- java堆:
用于存放对象实例和数组. - 方法区:
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据.
方法区中包含运行时常量池,Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。 -
直接内存:
并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域.(堆外内存)
一个实例对象被创建的过程如下图所示:
对象的内存布局
- 对象头
对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码 (HashCode),GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等.
对象头的另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
另外,如果对象是一个Java数组,那在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中却无法确定数组的大小。 - 实例数据
实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内存。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。 - 对齐填充
对象的大小必须是8字节的整数倍,当不符合时,由占位符填充来保证.
对象的访问定位
引用有两种:
-
句柄:
句柄包含了对象实例指针和对象类型指针
-
直接指针:
对象头中有对象类型指针