JVM之Java内存结构(一)

Java运行时数据区域

数据区域主要分为五个部分: - 程序计数器 - 虚拟机栈 - 本地方法栈 - 堆 - 方法区

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

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

线程独享的区域

程序计数器

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

### Java虚拟机栈 虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于存放以下内容: - 局部变量表 - 操作数栈 - 动态链接 - 方法出口

其中局部变量表包括以下内容: - 八大基本数据类型 - 对象引用 - returnAddress类型(保存的是return后要执行的字节码的指令地址)

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

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

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

本地方法栈

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

线程共享的区域

主要特点:

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

方法区

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

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

方法区内的主要回收目标是对常量池的回收和对类型的卸载。

运行时常量池

运行时常量池是方法区的一部分。主要存放以下内容: - 类信息(版本,字段,方法,接口等描述信息) - 常量池表(用于存放编译期生成的各种字面量与符号引用)

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

直接内存(堆外内存)

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


JVM之Java内存结构(一)
https://l1n.wang/2020/JVM/jvm01-java-memory-structure/
作者
Lin Wang
发布于
2020年3月21日
许可协议