java运行时数据区域总结


线程私有

        程序计数器:它是一块较小的内存区域,可以把它看作当前线程所执行的字节码的行号指示器。如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是native方法,这个计数器值则为空。注意:此内存区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError情况的区域

        Java虚拟机栈:Java虚拟机栈和线程的生命周期相同,每一个方法被调用直至执行完成的过程,就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。虚拟机栈中局部变量表说明



    • 局部变量表存放编译期可知的各种(8种)基本数据类型和对象引用、returnAddress类型(指向一个字节码指令的地址)。

    • 局部变量表中64位的long和double类型的数据会占用2个局部变量空间,其它类型占用一个;局部变量表所需的内存空间在编译期完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

        本地方法栈:本地方法栈是为虚拟机使用到的Native方法服务的,它和Java虚拟机栈很相似,只是服务对象不同。




非线程私有

        Java堆:Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。它是被所有线程共享的一块区域,在虚拟机启动时创建。Java堆唯一目的就是存放对象实例,几乎所有对象实例都存放在这里。Java堆可以分为新生代和老年代,而 新生代包含Eden空间、From Survivor空间、To Survivor空间。内存划分的目的是更好地回收内存。

        方法区:方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。它就是Non-Heap(非堆)(注意:因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用 永久代 来实现方法区,而对于其它虚拟机来说不存在 永久代 的概念)。这个区域回收目标主要是针对常量池的回收(废弃常量)和对类型的卸载(无用的类),但对它的回收效果很难让人满意;当方法区无法满足内存分配时,会抛出OutOfMemoryError异常。

        运行时常量池:它是方法区的一部分,它用于存放编译期生成的各种字面量和符号引用;它具有动态性,如:在运行期间也可能将新的常量放入池中,使用String类的intern()方法可以将字符串变量值放入常量池中。运行时常量池作为方法区的一部分,会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。(String类的intern()方法使用说明:String a="test"中a的地址是常量池的地址;String b = new String("test")中b是java堆中变量的存储地址;b = b.intern();表示将b的值存储为字符串变量在常量池中的地址

        直接内存:它不是虚拟机运行时数据区的一部分。为避免在Java堆和Native堆中来回复制数据,在JDK1.4中新加入了NIO类,引入基于通道和缓存区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByBuffer对象作为这块内存的引用进行操作。


对象访问说明

对象访问的主流访问方式:使用句柄(包含对象的实例数据和类型数据的具体地址信息,优点是句柄中存储的是稳定的句柄地址)和直接指针(对象地址,优点是访问对象速度快,它是应用最广的一个)。



参考书目《深入理解java虚拟机 ,作者 周志明著》