JVM运行时数据区域
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。分别是方法区、虚拟机栈、本地方法栈、堆、程序计数器。
程序计数器
字节码解释器工作时通过改变程序计数器的值来选取下一条需要执行的字节码指令。
每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响。因此程序计数器为“线程私有”内存。
虚拟机栈
每个方法在执行时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法进出口等信息。
本地方法栈
本地方法栈为Java虚拟机使用Native方式服务
Java堆
此内存区域的唯一目的用来存放对象实例。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。
Java堆是垃圾收集器管理的主要区域,因此也称为“GC堆”。
方法区
用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是各个线程所共享的内存区域。
方法区中还存在一个常量池,例如String对象则是存储于常量池中。当创建一个String对象时首先会去常量池中查找,如找到则直接返回该对象的引用,否则将此对象加入常量池后,返回该对象的引用。
JVM如何创建对象
虚拟机在遇到一条new指令时干了什么事?
- 检查指令的参数是否在常量池中定位到一个类的符号引用,并且该符号引用所代表的类已被加载、解析和初始化。
- 虚拟机为对象分配内存。
- 虚拟机对对象进行必要的设置。在对象的对象头中设置该对象为哪个类的实例、对象的哈希码、对象的GC分代年龄等信息。
执行完以上三步后,一个对象就已经产生,但从Java程序的视角看,该对象还需要进行init操作后,一个真正可用的对象才真正产生。
对象分配内存方式有两种:
1.指针碰撞(Java堆内存是规整的)
将已经用过的内存和空闲的内存各自放在一边,中间有个指针为二者分界线,当在空闲区分配一块内存后,指针向空闲空间挪动一段与对象大小相等的距离。
2.“空闲列表”(Java堆内存是不规整的)
虚拟机维护一个列表,记录可用的区域,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
对象的访问定位
Java程序在堆上创建对象实例,在栈上通过reference数据来操作堆上的具体对象。目前主流的访问方式有使用句柄和直接指针两种。
使用句柄: Java在堆上创建句柄池,池中包含了对象们的实例数据与类型数据的具体地址,并且在栈的reference数据中存储各个对象的句柄地址。
直接指针: 栈上reference数据直接存储对象地址。
区别:使用句柄当对象被移动时,只会改变句柄池中的实例数据指针,而reference本身不需要修改。使用直接指针则访问速度更快,节省了一次指针定位的时间开销。
更多了解,还请关注我的个人博客:www.zhyocean.cn