JAVA内存区域

neicun
最近开始学习JVM,在此把学习中的体会记录下来。资料主要来源是《深入理解JAVA虚拟机》这本书,以及一些网上找的其他资料。目标JVM是HotSpot

1、JAVA运行时数据区包括程序计数器、堆、虚拟机栈、本地方法栈、方法区

2、内存区域可以分为“线程私有”和“线程共享”2种。比如程序计数器是属于线程私有的,为了在线程切换之后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器;而堆是线程共享的

3、程序计数器是一块较小的内存空间,可以看作是当前线程执行的字节码的行号指示器。如果线程正在执行一个JAVA方法,该计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行一个NATIVE方法,该计数器值为空

4、虚拟机栈也是线程私有的,生命周期与线程相同

5、虚拟机栈描述的是JAVA方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

6、虚拟机栈中的局部变量表,存放编译期可知的各种基本数据类型、对象引用和returnAddress类型

7、本地方法栈与虚拟机栈类似,区别在于虚拟机栈为JAVA方法(也就是字节码)服务,而本地方法栈为虚拟机用到的NATIVE方法服务

8、鉴于本地方法栈和虚拟机栈十分相似,HotSpot直接将本地方法栈和虚拟机栈合二为一

9、JAVA堆是线程共享的内存区域,在虚拟机启动时就创建。该内存区域的唯一目的是存放对象实例,几乎所有的对象实例都在这里分配内存

10、JAVA堆也是垃圾收集器管理的主要区域,所以有时也被称为GC堆。JAVA堆可以细分为新生代和老年代;也可以进一步细分为Eden空间、From Survivor空间、To Survivor空间

11、方法区也是线程共享的内存区域。它用于存放已经被虚拟机加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据。虽然JAVA虚拟机规范将方法区描述为堆的一个逻辑部分,但是它却有一个别名叫NonHeap,目的应该是与JAVA堆区分开来

12、方法区的内存回收目标主要是针对常量池的回收和对类型的卸载

13、运行时常量池是方法区的一部分。Class文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载之后存放到方法区的运行时常量池中

14、即使是最简单的访问,也会涉及JAVA栈、JAVA堆、方法区这3个最重要的内存区域之间的关联关系

1
Object obj = new Object();

Object obj这部分的语义会反映到虚拟机栈的本地变量表中,作为一个reference类型数据出现

new Object()这部分的语义会反映到JAVA堆中,形成一块存储了Object类型所有实例数据值的结构化内存

在JAVA堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中

15、由于reference类型在JAVA虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过什么方式去定位,以及访问JAVA堆中的对象的具体位置,所以不同虚拟机实现的对象访问方式会有所不同。主流的访问方式有2种:使用句柄,和直接指针

16、如果使用句柄访问方式,JAVA堆中会划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含对象实例数据和类型数据各自的具体地址信息

17、如果使用直接指针访问方式,JAVA堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象地址

18、使用句柄访问方式的好处是reference中存储的是稳定的句柄地址,在对象被移动时(比如在垃圾收集的时候)只会改变句柄中的实例数据指针,reference本身不需要修改。使用直接指针访问方式的好处是速度更快,节省了一次指针定位的时间开销,由于对象的访问在JAVA中非常频繁,因此这类开销积少成多以后也相当可观

19、HotSpot是使用直接指针访问方式进行对象访问的