博客内容为深入理解Java虚拟机第二章读书笔记。
Java运行时数据区域
Java运行时数据区域主要包括了程序计数器、Java虚拟机栈、本地方法栈、堆。
程序计数器
学习过计算机原理相关课程对程序计数器还是很熟悉的。Java虚拟机的程序计数器可以看作当前线程所执行字节码的行号指示器。注意程序计数器是对线程私有的,即每个线程都需要一个独立的程序计数器。如果线程正在执行的是Native方法,则程序计数器值为空。
Java虚拟机栈
Java虚拟机栈为线程私有,描述的是Java方法执行的内存模型:每个方法在执行时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个Java方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈过程。
本地方法栈
本地方法栈与虚拟机栈类似,只是本地方法栈为虚拟机使用到的本地方法服务。
Java堆
Java堆简单说就是用来存放对象实例,是所有线程所共享的。Java堆从内存回收角度可以分为新生代和老年代,再细分有Eden空间、From Survivor空间、To Survivor空间等。Java虚拟机规范规定Java堆可以处于物理上不连续的内存空间,只要逻辑上连续即可。
方法区(永久代)
方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,为各个线程所共享。很多人应该在开发时对永久代内存溢出深有体会(OutOfMemory:PermGen space),很多时候只有调大方法区的大小来解决了。注意的是在JDK7中将原本在永久代的字符串常量池移除了,而JDK8取消了永久代,永久代相关数据被移到了一个与堆不相连的本地内存区域(元空间)。
Java对象创建与访问定位
对象创建
对象创建自然涉及到内存的分配。主要包括指针碰撞(内存规整)和空闲列表(内存不规整)两种方法。为解决内存分配的线程安全问题,有两种解决方案,一种是对分配内存空间进行同步处理;另外一种是每一个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲(TLAB)。
内存分配完后虚拟机将分配到的内存空间都初始化零值。接下来虚拟机对Java对象的对象头进行必要设置,如类元数据信息、对象的哈希码、对象的GC分代年龄等信息。然后需要执行init方法对对象进行初始化。
对象的定位访问
Java程序通过栈上的引用数据来操作堆上的具体对象。对象访问方式包括句柄和直接指针两种。
句柄就是在Java堆上划分出一块内存作为句柄池,reference存储的是对象的句柄地址,句柄中包含了对象的实例数据和类型信息的各自地址信息。优点是队形被移动时reference值不需要修改。
直接指针中reference中存放的直接就是对象地址,需要考虑的是如何放置访问类型数据的相关信息。优点是访问速度更快,节省了一次内存定位的时间开销。