参考资料:《深入理解Java虚拟机(第2版)》
概述
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途。根据Java虚拟机规范(JavaSE 7版),Java虚拟机所管理的内存将会包括以下几个运行时数据区域:
程序计数器
一块较小的内存空间,可以被看作是当前线程所执行的字节码的行号指示器。字节码解释器通过改变它来选择下一条需要执行的字节码指令。
虚拟机栈
为虚拟机执行Java方法(也就是字节码)服务,生命周期与线程相同。Java方法执行时会创建一个栈帧,用于存储 局部变量表、操作数栈、动态链接等信息。方法自调用到执行结束,对应着一个栈帧在虚拟机栈中入栈到出栈。
本地方法栈
与虚拟机栈很类似,区别在于它不是为Java方法服务,而是为Native方法服务。
堆
在虚拟机启动时创建,存放对象实例。在存储空间上逻辑连续,物理不一定连续。
对于hotspot虚拟机来说,Class对象比较特殊,它存放在方法区里。
方法区
存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
运行时常量池
方法区的一部分。与Class文件常量池的不同在于,运行时常量池还允许运行期间而不仅仅是编译期间置入新的常量。例如String类的intern()方法。
String类的intern方法:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
Class文件常量池
主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)
直接内存
不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。它是一种堆外内存,通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
JVM创建对象的过程
虚拟机遇到new指令
→ 检查常量池中能否定位到一个类的符号引用
→ 检查该类是否已被加载、解析、初始化(若没有则加载)
→ 为对象分配内存(两种方法:指针碰撞or空闲列表)
→ 初始化该内存为零值
→ 设置对象头(哈希码,GC分代年龄,从属于什么类等)
→ 执行方法,将对象按照程序员的意愿进行初始化。
对象的内存布局
对象头Header(包含各种元数据:哈希码、GC分代年龄等等,另外还有类型指针)
实例数据Instance Data(程序代码中所定义的各种类型的字段内容)
对齐填充Padding(使对象大小保持8字节的整数倍)
对象的访问定位方式
使用句柄 or 直接指针
使用句柄:
直接指针: