JVM内存模型

JVM内存模型

JVM在执行Java程序时将它所管理的内存分为若干个不同的数据区域。这些区域有各自的用途以及创建和销毁时间。这些区域如下图所示:
JVM运行时数据区

程序计数器

程序计数器是线程私有的。它记录当前线程所执行的字节码,字节码解释器通过改变这个计数器的值来选取下一条要执行的字节码。
如果线程正在执行的是一个Java方法,那么它记录的是正在执行的虚拟机字节码指令的地址;如果线程正在执行的是本地(Native)方法,那么它的值应为空(Undefined)。

Java虚拟机栈

Java虚拟机栈是线程私有的 。每个方法被执行的时候,Java虚拟机会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

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

这些数据在局部变量表中的存储以局部变量槽来表示,double和long占两个变量槽,其余的都只占一个变量槽。局部变量表的内存分配是在编译器完成的,方法运行期间不会改变它的大小。

本地方法栈

本地方法栈是线程私有的。作用和虚拟机栈类似,区别只是虚拟机栈为虚拟机执行Java方法服务,而本地方法栈为虚拟机使用到的本地方法服务。

有的Java虚拟机(如HotSpot虚拟机)直接把本地方法栈和虚拟机栈合二为一。

Java堆

Java堆是所有线程共享的一块内存区域。该内存区域的唯一目的就是存放对象实例。《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配“。

Java堆是垃圾收集器管理的内存区域。要注意的是,新生代,老年代,永久代,Eden,From Survivor,To Survivor等名称所代表的区域仅仅是一部分垃圾收集器的共同特性或者说设计风格,而非某个Java虚拟机具体实现的固有内存布局。

Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该 被视为连续的。它可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。

方法区

方法区也是各个线程共享的内存区域。它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

JDK8之前很多使用HotSpot虚拟机的Java程序员习惯把方法区称为”永久代“,或者将两者混为一谈。本质上两者是不等价的,只是当时的HotSpot虚拟机设计团队把收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区而已。在JDK 6的时候HotSpot开发团队就有放弃永久代,逐步改为采用本地内存(Native Memory)来实现方法区的计划了,到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了 JDK 8,终于完全废弃了永久代的概念,改用在本地内存中实现的元空间(Metaspace)来代替,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。

垃圾收集在方法区是比较少出现的,主要是针对常量池的回收和对类型的卸载。

运行时常量池

运行时常量池是方法区的一部分。Class文件中的常量池表是用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。此外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。

运行时常量池具备动态性,在运行期间也可以将新的常量放入池中,如String类的intern()方法。

PS: 直接内存

直接内存并不是并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域,但是这部分内存也被频繁地使用。

本机直接内存的分配不会受到Java堆大小的限制,但是还是会受到本机总内存(包括物理内存、SWAP分区或者分页文件)大小以及处理器寻址空间的限制。

posted @ 2021-12-25 22:57  aosrc  阅读(38)  评论(0)    收藏  举报