JVM之Java内存结构(一)

1. Java运行时数据区域

数据区域主要分为五个部分:

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈
  • 方法区

其中所有线程共享区域有:方法区和堆。

每个线程独享区域有:虚拟机栈,本地方法栈,程序计数器
Java运行时数据区域

1.1 程序计数器

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,如果当前线程执行的是一个本地方法,那么此时计数器的值为undefined,是唯一一个不会出现OutOfMemoryError的内存区域。

1.2 Java虚拟机栈

虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存放以下内容:

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法出口

其中局部变量表包括以下内容:

  • 八大基本数据类型
  • 对象引用
  • returnAddress类型(保存的是return后要执行的字节码的指令地址)

这些数据类型在局部变量表中的存储空间以局部变量槽来表示,64位长度的longdouble类型占用两个变量槽,其余的占用一个。局部变量表所需的内存空间在编译期间完成分配,方法运行期间不会改变局部变量表的大小。

Java 虚拟机栈会出现两种异常:StackOverFlowErrorOutOfMemoryError

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverFlowError
  • 如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够内存会抛出OutOfMemoryError

1.3 本地方法栈

本地方法栈是为JVM使用到的Native方法准备的空间

1.4 堆

  • 堆是被所有线程共享的一块内存区域
  • 在虚拟机启动时创建
  • 堆的唯一目的就是存放对象实例
  • 堆是垃圾回收管理的内存区域
  • 可分为新生代,老年代,永久代等(一部分垃圾收集器的共同特性或者设计风格,不是具体实现的固有内存布局)
  • 从分配内存的角度看,所有线程共享的Java堆可以划分出多个线程私有的分配缓冲区(TLAB)以提升对象分配的效率

1.5 方法区

Java 虚拟机规范中定义方法区是堆的一个逻辑部分。
用于存储以下内容:

  • 已被虚拟机加载的类信息
  • 常量
  • 静态变量
  • 即时编译器编译后的代码

在JDK8之前,方法区也称为永久代。
方法区内的主要回收目标是对常量池的回收和对类型的卸载。

1.5.1 运行时常量池

运行时常量池是方法区的一部分。主要存放以下内容:

  • 类信息(版本,字段,方法,接口等描述信息)
  • 常量池表(用于存放编译期生成的各种字面量与符号引用)

Java并不要求常量一定在编译期间产生,并非Class文件中常量池的内容才能进入运行时常量池,运行期间产生的新的常量也能放入池中。比如String类的intern()方法。
当常量池无法再申请到内存会抛出OutOfMemoryError

1.6 直接内存(堆外内存)

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是会被频繁地使用,也可能导致OutOfMemoryError
直接内存的分配不受JVM控制,但是会受到本机总内存(物理内存,SWAP分区,分页文件)以及处理器寻址空间的限制。

文章作者: L1nker4
文章链接: https://l1n.wang/jvm01-java-memory-structure/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 L1nker4