首页 JVM 内存区域
文章
取消

JVM 内存区域

概述

jvm 内存区域整体分为5个部分:

  1. 程序计数器
  2. Java 虚拟机栈
  3. 本地方法栈
  4. Java 堆
  5. 方法区

程序计数器(Program Counter Register)

程序计数器可以看作是当前线程执行的字节码的行号指示器。Java 虚拟机的多线程是通过轮流切换并分配处理器的执行时间的方式来实现的,为了保证每个线程切换后恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们成这类内存为线程私有的内存。唯一一个在Java虚拟机中没有规定任何OutOfMemoryError 情况的区域。

Java 虚拟机栈(Java Virtual Machine Stacks)

线程私有,生命周期与线程相同,描述的是 Java 方法执行的内存模型,每个方法的执行都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈找那个入栈到出栈的过程。

将 Java 内存模型分为堆内存和占内存的分发比较粗糙,其中“栈”指的就是虚拟机栈或者说是虚拟机栈中局部变量表部分。局部变量表存储了编译期可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,不同于对象本身,可能是一个指向对象起之地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和 returnAddress 类型(指向了一条字节码指令的地址) ,其内存空间在编译期间完成分配。

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

本地方法栈(Native Method Stack)

本地方法栈的作用与虚拟机栈的作用相似,区别在于虚拟机栈为虚拟机执行的 Java 方法(也就是字节码)服务,本地方法栈为虚拟机使用的 native 方法服务。虚拟机规范中没有对其使用的语言,使用的方式与数据结构进行强制规定。有的虚拟机甚至将其与虚拟机栈合二为一,与虚拟机栈一样,本地方法栈也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

Java 堆(Java Heap)

Java 堆是 Java 虚拟机管理的最大的一块内存,被所有线程所共享。唯一的目的便是存放对象实例。垃圾回收的主要区域,被称为GC堆(Garbage Collected Heap)。从内存回收的角度来看,由于现在的收集器基本都采用分带收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Surivor空间、To Surivor空间。从内存分的的角度来看,线程共享的 Java 堆可能划分出多个线程私有的分配缓冲区(Threal Loal Allocation Buffer,TLAB)。无论怎么如何划分,都与存放的内容无关,无论哪个区域,存放的都仍然是对象实例,进一步划分的目的是为了更好的回收内存,或者更快的分配内存。

Java 堆在物理上可以是非连续的空间,只要逻辑上是连续的即可,既可以实现成固定大小的,也可以是可扩展的,通过 —Xmx 和 —Xms 控制。如果堆中没有内存完成实例分配,并且堆也无法扩展时,将会抛出 OutOfMemoryError 异常。

方法区(Method Area)

被各个线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,虽然 Java 虚拟机规范把方法去描述为对的一个逻辑部分,但是他有一个别名叫做 Non-Heap(非堆),目的是与 Java 堆区分开来。很多人把方法区成为永久代,但是两者并不等价!永久代是方法区的实现!当方法区无法完成内存分配需求时,将抛出 OutOfMemoryError 异常。

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中的常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在防范去的运行时常量池中存放。其受到方法区内存的限制,当常量池无法在申请到内存时会抛出 OutOfMemoryError 异常。

本文由作者按照 CC BY 4.0 进行授权

Cookie & ThreadLocal 代替 session 实现无状态登录

Java 集合体系学习