拒绝做知识“收藏家”

答应我,跟我一起学习吧,别再做知识收藏家了,把《深入理解 Java 虚拟机》书拿出来,翻它,盘它,磋磨它。

你创建的 Java 对象搁哪了

你创建的 Java 对象搁哪了 - 图1

今天学习的内容是 JVM 自动内存管理中的 运行时数据区域 内容。

自动内存管理

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来。 —— 出自周志明《深入理解 Java 虚拟机》

Java 的内存区域与内存溢出。

对于 Java 程序来讲,内存是由虚拟机的内存自动管理机制垃圾回收来进行管理的。这样便使得程序不容易出现内存溢出和内存泄漏问题。但是一旦出现内存问题,排查和解决起来便不是一件容易的事。

运行时数据区域

  • 程序计数器
  • 虚拟机栈
  • 本地方法栈
  • 方法区 - 运行时常量池
  • 直接内存

PC寄存器(程序计数器)

  • 线程私有
  • 存储当前方法的字节码位置,如果是本地方法则存储 undefined
  • 唯一一个不会出现 OOM 的位置
  • 方法执行时使用;

Java 虚拟机栈

  • 线程私有
  • 存储程序方法执行时的 栈帧
  • 达到栈允许最大容量抛出 StackOverflow Error 栈溢出
  • 允许动态扩展,申请不到内存时抛出 OOM。(允许动态扩展由虚拟机实现方自行选择,hotspot选择的则是不允许动态扩展)
  • 不需要保证连续的内存
  • 方法执行时使用;

注:在 hotspot 中不允许扩展栈内存,但同样会发生 OOM ,这是发生在创建线程内存申请时内存不足抛出的OOM,这里也属于栈原因引起的。

本地方法栈

同上

注:说明两者服务对象,虚拟机栈服务于 Java 程序,本地方法栈服务于 JVM 程序(native 方法)。

  • 线程共享
  • 存储类实例、数组对象
  • 容量超过允许最大值时抛出 OOM 异常(允许动态扩展)
  • 不需要保证连续的内存
  • 创建对象时使用

方法区

  • 线程共享
  • 存储类的结构信息(方法、字段、构造函数)、运行时常量池
  • 容量超过允许最大值时抛出 OOM 异常(允许动态扩展)
  • 不需要保证连续的内存
  • 虚拟机启动时创建
  • 后被替换为元空间(这里的内容要和 hotspot 的“永久代“一起理解)

运行时常量池

(这部分内存区域同在方法区中体现)

  • 线程共享
  • 存储接口或类的常量池(字面量 与 符号引用类的全部限定名等信息
  • 类加载时创建
  • 创建过程使用的方法区内存大小,可能出现 OOM 异常

直接内存

(这部分为延展内容)

  • 不受虚拟机参数控制,直接使用主机内存,受主机内存大小限制;
  • JDK4中的 NIO 首次使用;
  • 在设置JVM参数时,需考虑直接内存的使用大小,防止其过渡使用出现 OOM
  • JDK7的时候,使用直接内存实现了方法区,到 JDK8 将 JDK 7 剩余的类型信息移入元空间(这里指 hotspot 虚拟机) | 作用\名称 | PC寄存器(程序计数器) | Java虚拟机、本地方法栈 | Java 堆 | 方法区 | | —- | —- | —- | —- | —- | | 异常情况 | 无 | 栈溢出、堆溢出 | 堆溢出 | 堆溢出 | | 需要连续内存 | 否 | 否 | 否 | 否 | | 存储内容 | 在执行 Java 代码时,存储字节码地址。
    在执行本地方法时,存储 undefined | 栈帧(每个栈帧以方法为单位) | 类实例、数组对象 | 类的结构信息、字段、方法等 | | 使用时机 | 方法执行时 | 方法执行时 | 创建对象时 | 类被加载时 | | 线程私有 | 是 | 是 | 否 | 否 |

写在最后

Java 的对象搁哪现在你清楚了吗?每天一点点,啃完虚拟机。欢迎加群一起讨论学习,大家一起学习效率很高哦