01.内存结构

JVM内存结构

JVM是典型的基于栈的架构,不同于Android操作系统设计的Dalvik虚拟机采用的基于寄存器的架构,其在单运算的操作次数上会多于基于寄存器的架构。

JVM 内存结构

Java虚拟机会将内存分为几个不同的管理区,这些区域各自有各自的用途,根据不同的特点,承担不同的任务以及在垃圾回收时运用不同的算法。总体分为下面几个部分:

  • 堆(Heap:所有的对象实例和数组,根据虚拟机规范的规定,Java堆可以是固定的大小也可以是按照需求动态扩展的,而且不需要保证是连续的。

  • 元空间(Metaspace:类的结构信息,如类的字段、方法、接口、构造函数,还有运行时常量池等。在Java 8之前,PermGen是存放在堆中,在Java 8之后,PermGenMetaspace替代,并且Metaspace直接存放在原生内存,而不再是堆中。

  • 程序计数器(Program Counter Register:如果线程执行的是一个Java方法,那么寄存器里面记录的就是正在执行的虚拟机字节码指令的地址,如果线程执行的是一个native方法,那么寄存器记录的值为undefined。程序计数寄存器也是虚拟机规范里面唯一一个没有规定任何OutOfMemoryError情况的区域。

  • 虚拟机栈(JVM Stacks:局部变量表、操作数栈、方法出口等信息,局部变量表存放了编译时期可知的各种基本数据类型、对象引用和指向了一条字节码指令的地址。

  • 本地方法栈(Native Method Stacks:局部变量表、操作数栈、方法出口等信息。

JVM 运行时数据区域

除此之外,还有JVM内存管理之外的一个内存区:直接内存。在JDK1.4中新加入类NIO类,引入了一种基于通道与缓冲区的IO方式,它可以使用Native函数库直接分配堆外内存,即我们所说的直接内存,这样在某些场景中会提高程序的性能。

Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。每当创建一个新的线程时,JVM会为该线程创建一个虚拟机栈,同时会为这个线程分配一个PC寄存器,并且这个PC寄存器会指向这个线程的第一行可执行代码。每当调用一个新方法时会在这个栈上创建一个新的栈帧数据结构,这个栈帧会保留这个方法的一些元信息,如在这个方法中定义的局部变量、一些用来支持常量池的解析、正常方法返回以及异常处理机制等等。

如果线程清求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会拋出OutOfMemoryError异常。