JVM内存模型

JVM内存模型

Scroll Down

概述

java虚拟机会在运行Java进程的时候,会将进程内存分为若干个不同的内存区域。主要分为两块。一块随着虚拟机进程的启动而一直存在的公共内存空间 如 方法区(Method Area)和 (Heap)。另外一块依赖用户线程的启动和结束而建立和销毁 如 虚拟机栈(VM Stack)和 程序计数器(Program Counter Register)

15168036-6072bfced465f0ab

程序计数器

程序计数器是一块较小的内存空间,和汇编中的PC寄存器是一样的,用于储存当前线程所执行的行号。字节码解释器工作时就算通过改变这个计数器的值来选取下一条要执行的字节码指令进行执行。程序的流程中,分支、循环、跳转、异常等等基本功能都是由该计数器完成

由于JVM虚拟机是通过多线程轮流切换,分配处理器运行时间来实现的,所以每一个线程都有自己的执行字节码指令,相互独立。因此,为了线程切换后能够恢复到正确的执行位置,每个线程都需要有自己的一个独立的程序计数器。所以程序计数器是线程私有的内存空间

如果线程正在执行一个java方法,那计数器记录的是正在执行的字节码指令地址,如果执行的是本地方法(C++),那么这个计数器值为空

Java虚拟机栈

java虚拟机栈也是线程私有的,栈空间的生命周期和线程相同,虚拟机栈描述的是Java方法执行的线程内存模型,每一个方法被执行的时候,java虚拟机都会同步创造一个栈帧,用于储存局部变量表、操作数、动态连接、方法出口等信息。每一个方法被调用到执行完毕的过程,就对应着一个栈帧入栈到出栈的过程。

局部变量表

局部变量表存放了java基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,用于指向一个对象地址的指针)和returnAddres类型(指向一条字节码指令地址)

在局部变量表中,只有long double占用个变量槽(变量槽大小具体看java虚拟机位数),其余变量只占一个变量槽。

局部变量表的内存空间在编译期就已确定。进入一个方法后,这个方法需要在栈帧中分配多大的局部空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

本地方法栈

和虚拟机栈一样,只不过java虚拟机栈运行的是java方法,而本地方法栈运行的是本地方法(C++)

Java堆

Java堆是虚拟机所管理的内存最大的一块区域。java堆是被所有线程共享的一块内存区域,生命周期和虚拟机一致,在虚拟机启动时创建,该内存的唯一目的是存放对象实例,java世界所有的实例对象都是在这里分配内存。

java堆是垃圾收集器管理的内存区域,因此一些资料中它也被称为GC堆(新生代、老年代、永久代等等)。

所有线程共享的java堆中可以划分出多个线程私有的分配缓存区,以提升对象分配时的效率,

java堆可以被实现成固定大小,也可以用过 -Xmx 和-Xms进行设定

当堆满溢出时,jVM将会抛出OutOfMemoryError异常

方法区

方法区域和java堆一样,也是线程共享的内存区域,它用于储运已被虚拟机加载的类型信息、常量、静态变量、编译器编译后的代码缓存数据等

在方法区中,垃圾收集形为在这个区域非常少收集,数据一旦进入,除非针对常量池回收和对类型的卸载,不然垃圾回收基本不触发

方法区满溢出时。也会抛出OutOfMemoryError异常

运行常量池

运行常量池是方法区的一部分,class文件中除了对类版本、字段、方法、接口等描述信息,还有一项是常量池表,用于存放编译器生成的各自字面量和符号引用,这部分会被类加载后放到方法区的运行时常量池

运行时常量池另外一个重要特征是具备动态性,该常量不许要编译期之时就产生

直接内存

直接内存属于jvm虚拟机运行时数据区之外的内存区域,

在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的 DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了 在Java堆和Native堆中来回复制数据。

因此,直接内存不受jvm虚拟机内存限制,只会受本机物理内存限制