Java 内存区域

2021-08-11 1206点热度 0人点赞 0条评论

运行时数据区

参考 JDK 8 的官方文档,Java 虚拟机运行时数据区主要由以下部分组成:

  • 程序计数器(Program Counter Register)
  • Java 虚拟机栈(JVM Stack)
  • 本地方法栈(Native Method Stack)
  • Java 堆(Java Heap)
  • 方法区(Method Area)
  • 运行时常量池(Runtime Constant Pool)
jvm-runtime-data-area

程序计数器

  • 程序计数器是一块较小的内存空间,用于指示当前线程执行字节码的行号;
  • 程序计数器是线程独享的存储空间,生命周期与线程相同;
  • 每个线程都有独立的程序计数器,互不干扰,是实现线程并发的基础条件。

当线程正在执行 Java 方法时,PC 指向字节码指令地址;当执行的是 Native 方法时,指针为空(Undefined)。

此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

Java 虚拟机栈

本地方法栈

  • 线程私有空间,生命周期与线程相同;
  • 功能与 Java 虚拟机栈相似,区别是本地方法栈服务对象是 Native 方法;
  • 相似条件下也会抛出 StackOverflowError 和 OutOfMemoryError 异常。

Java 堆

  • 线程共享空间,生命周期与虚拟机相同;
  • 虚拟机所有的实例几乎都在 Java 堆上分配;
  • Java 堆存放的空间不要求物理上的连续,逻辑上连续即可;
  • 当 Java 堆内存不足以分配实例且无法再扩展时,会抛出 OutOfMemoryError 异常。

方法区

  • 线程共享空间,生命周期与虚拟机相同;
  • 用于存放虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据;
  • 当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。

早期的 JDK 中,HotSpot 虚拟机选择把收集器的分代设计扩展至方法区,或者说使用永久代来实现方法区。回过头来看,这并不是一个好主意,这种设计导致了 Java 应用更容易碰到 OutOfMemory 的问题(永久代有-XX:MaxPermSize 的上限,即使没有设置也有默认大小)。

在 JDK 6 时候,HotSpot 就已经提出了放弃永久代,逐步改用本地内存(Native Memory)来实现方法区的计划了(JEP 122)。 到了 JDK 7,已经把原本放在永久代的字符串常量池与静态变量等移出,而到了 JDK 8,终于完全废弃了永久代的概念,在本地内存中实现的元空间(Metaspace)来代替。

运行时常量池

  • 运行时常量池是方法区的一部分;
  • 存放 Class 文件中的常量(final static 修饰的 ConstantValue),编译期产生;
  • 存放运行时产生的常量,比如 Strin.intern()方法;
  • 内存方面受到方法区的内存限制,内存不足时抛出 OutOfMemoryError 异常。

直接内存

直接内存(Direct Memory)不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机规范中定义的内存区域。

  • 在使用 NIO 时,通过 Native 函数库直接分配堆外内存,然后通过存储在 Java 堆中的 DirectByteBuffer 对象操作这块内存;
  • 直接内存分配不受 Java 堆大小的限制,但受到机器总内存的限制;
  • 当无法分配到内存时会抛出 OutOfMemoryError 异常。

SilverLining

也可能是只程序猿

文章评论