一 . 线程隔离的数据区

    1. 程序计数器(Program Counter Register):是线程执行时的行号指示器,当虚拟机执行字节码文件时,用来标识字节码执行到第几行,是线程隔离的,各个线程之间的计数器不互相影响,所以这块内存区域是“线程私有”的。如果正在执行的是Native方法,则这个计数器数值为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。


    2. 虚拟机栈(Virtual Machine Stack):他的生命周期与线程相同,同样也是私有的。虚拟机栈描述的是Java方法执行的内存模型;每个方法在执行的同时都会创建一个栈帧(Stack Frame),用来存储局部变量表、操作数栈、动态链接、方法出口等信息。

    局部变量表存放着编译期可知的各种基本数据类型(boolean、byte、char、short、int、long、float、double)、对象引用和returnAddress类型。其中long和double类型的数据会占用2个局部变量空间(Slot),其余数据类型占用1个。局部变量表所需要的内存空间在编译期间完成分配,当进入一个方法方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小


    3. 本地方法栈(Native Method Stack):与虚拟机栈发挥的作用是相似的,他们之间的区别是虚拟机栈为虚拟机执行Java方法服务,本地方法栈则为虚拟机使用到的Native方法服务。虚拟机栈和本地方法栈同样都会抛出StackOverflowError和OutOfMemoryError异常。


二 . 线程共享的数据区

    1. 堆(Heap):是Java虚拟机管理的内存最大的一块区域,在虚拟机启动时创建。此区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。但随着JIT编译器的发展和逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,多有的对象在堆上分配也不是那么“绝对”了。

    堆是垃圾收集器管理的主要区域。Java堆和可细分为:“新生代”“老年代”;再细致一点的有Eden空间、From Survivor空间、To Survivor空间等。从内存分配的角度看,线程共享的堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。但不管怎么划分,堆中存储的仍然是对象实例。如果堆中没有内存完成实例分配,并且堆也无法扩展,则会抛出OutOfMemoryError异常。


    2. 方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但他却又一个别名叫Non-Heap。


PS:方法区还被成为“永久代”,实际上两者并不等价。hotspot的方法区存放在永久代中,因此方法区被人们称为永久代。永久代的垃圾回收主要包括类型的卸载和废弃常量池的回收。在目前已经发布的JDK1.7的HotSpot中,已经把原本放在永久代的字符串常量池移出。

    和Java堆一样,不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集。相对而言,垃圾收集行为在这个区域是比较少见的,但并非数据进入了方法区就如永久代的名字一样“永久”的存在了。详细见://TODO


参考文献:《深入理解Java虚拟机 JVM高级特性与最佳实践》 -- 周志明

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐