JVM学习01——内存区域及内存溢出

本文详细介绍了Java虚拟机(JVM)的内存管理机制,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、运行时常量池和直接内存等七个运行时数据区域的作用、特性和异常情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行时数据区域

  Java虚拟机在执行Java程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动和结束而建立和销毁。根据《Java虚拟机规范(Java SE 7版)的规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域。
这里写图片描述
1.程序计数器(Program Counter Register)

  • 程序计数器是一块较小的内存空间,它可以被看作是当前线程所执行的字节码的行号指示器。
  • 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间来实现的。因此,为了线程切换后能回到正确位置,每条线程都需要一个单独的程序计数器,称这类内存区域为“线程私有的内存”。
  • 如果线程正在执行Java方法,则计数器记录字节码地址;如果执行Native方法,则计数器记录为空。所以是唯一一个不存在OutOfMemoryError情况的内存区域。

2.Java虚拟机栈

  • 线程私有,生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型,每个方法执行都会创建一个栈帧。
  • 栈帧(Stack Frame):用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放编译时的8中基本数据类型、引用类型和returnAddress类型(指向一条字节码指令地址)。
  • 该区域有两种异常:
    • 线程请求栈深度大于虚拟机栈深度,抛出StackOverflowError异常。
    • 动态拓展时无法申请到足够内存,抛出OutOfMemoryError异常。

3.本地方法栈
与虚拟机栈功能类似,但虚拟机栈为Java方法服务,而本地方法栈为Native方法服务。也有 StackOverflowError和 OutOfMemoryError异常。
4.Java堆

  • Java堆(Java Heap)是Java虚拟机管理的最大内存区域,虚拟机启动时创建,所有线程共享该内存。该内存唯一目的就是存放对象实例,几乎所有的对象实例都在此分配内存。
  • Java堆是垃圾收集器管理的主要区域,也被成为“GC堆”。Java堆可被细分为:新生代和老年代。
    • 新生代: 用来存放生命周期较短的对象,而新生代又使用复制算法进行GC ,又将其按照8:1:1的比例分为一块较大的Eden空间和2个较小的From Survivor和To Survivor空间。
    • 老年代:用来存放生命周期较长的对象。
  • Java堆可以处于物理上不连续内存区域,但需要逻辑上连续。
  • 如果堆中没有内存进行实例分配,并且堆也无法在拓展时(通过-Xmx和-Xms控制),将会抛出OutOfMemoryError异常。
    • Xmx Java堆最大内存,默认值为物理内存的1/4,当可用的Java堆内存大于70%时,JVM会将内存调整至-Xms所指定的初始值
    • Xms Java堆初始内存,默认值为物理内存的1/64,当可用的Java堆内存小于40%时,JVM会将内存调整至-Xmx所允许的最大值

5.方法区

  • 方法区和Java堆一样,是各个线程的共享的区域,它用于存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 可以处于物理上不连续内存区域,但需要逻辑上连续。
  • 该区域的垃圾收集比较少见,主要针对常量池的回收和类型的卸载。
  • 方法区无法满足内存分配需求时,会抛出OutOfMemoryError异常。

6.运行时常量池

  • 属于方法区的一部分。
  • 存放 Class 文件中的常量池(存放编译期生成的各种字面量和符号引用);翻译出来的直接引用;运行期间产生的新的常量(譬如 String 类的 intern() 方法)。
  • 常量池无法申请到内存, 会抛出OutOfMemoryError异常。

7.直接内存
这里写图片描述

  • 直接内存并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范定义中的内存区域。但这部分区域被频繁使用并可能引起OutOfMemoryError异常。
  • NIO(New Input/Output)类中 ,可用使用 Native 函数库直接分配堆外内存,然后通过一个存储在 java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。
  • 不受 java 堆大小的限制,但受本机总内存的大小及处理器寻址空间的限制,会抛出 OutOfMemoryError异常。

注:本文来源为我的《深入理解Java虚拟机-JVM高级特性与最佳实践》读书笔记,以及部分网络资料。

### JVM 内存区域及其触发 OutOfMemoryError 的可能性 #### 1. **堆内存 (Heap Memory)** 堆内存是 JVM 中最大的一块内存区域,用于存储对象实例。当堆内存不足以分配新对象,并且垃圾回收器无法释放足够的空间时,就会抛出 `OutOfMemoryError` 错误[^4]。 典型场景包括:频繁创建大对象、内存泄漏或者设置了过小的堆大小(通过 `-Xmx` 参数控制最大堆大小)。 ```java // 创建超大数据结构可能导致堆内存溢出 List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 每次增加1MB数据 } ``` --- #### 2. **方法区 / 元空间 (Metaspace)** 在 JDK 8 及之后版本中,永久代被移除并替换为元空间。元空间主要用于存储类的元信息(如字节码、运行时常量池等)。如果应用程序加载了大量的类文件而未设置合适的元空间上限,则可能会引发 `OutOfMemoryError: Metaspace` 错误[^3]。 常见情况有动态代理框架(如 Spring AOP)、OSGi 容器或微服务架构下频繁加载卸载模块的情况。 ```java // 动态生成大量类可能引起元空间溢出 for (int i = 0; i < Integer.MAX_VALUE; i++) { ClassLoader loader = new CustomClassLoader(); loader.loadClass("com.example.Class" + i); } ``` --- #### 3. **栈内存 (Stack Memory)** 每个线程都有自己的私有栈,用于保存局部变量表和操作数栈等内容。如果单个线程的方法嵌套层次太深或存在无限递归,就可能出现 `StackOverflowError`;但如果线程数量过多且每个线程占用较大的栈空间,则会因总栈容量不足而导致 `OutOfMemoryError`[^5]。 可以通过调整 `-Xss` 参数来改变每个线程的最大栈大小。 ```java // 无限递归导致栈溢出 public void recursiveMethod() { recursiveMethod(); // 调用自身直到耗尽栈空间 } recursiveMethod(); ``` --- #### 4. **本地直接内存 (Direct Memory)** 除了 Java 堆之外,程序还可以通过 `ByteBuffer.allocateDirect()` 分配非堆内存(即本地直接内存),这部分不受 JVM 堆管理约束。然而,默认情况下它的总量受到物理内存限制以及由参数 `-XX:MaxDirectMemorySize` 控制的阈值影响。一旦超出该范围便会报告 `OutOfMemoryError: Direct buffer memory` 错误。 ```java import java.nio.ByteBuffer; public class DirectMemoryTest { public static void main(String[] args) throws Exception { while (true) { ByteBuffer buf = ByteBuffer.allocateDirect(1024 * 1024); // 占用1MB直连缓冲区 } } } ``` --- #### 5. **本机内存 (Native Memory)** 虽然严格意义上不属于 JVM 自身定义的部分,但在实际应用过程中,诸如 JNI 或者第三方库也可能消耗大量的本机资源从而间接造成 OOM 现象。例如某些图像处理工具包需要额外申请外部缓存区完成渲染工作却未能及时释放这些临时占位符的话同样容易陷入困境之中。 --- ### 总结 综上所述,JVM 主要有以下几个部分会发生 `OutOfMemoryError`: - 堆内存 (`OutOfMemoryError: Java heap space`) - 方法区/元空间 (`OutOfMemoryError: Metaspace`) - 栈内存 (`OutOfMemoryError: unable to create new native thread`) - 直接内存 (`OutOfMemoryError: Direct buffer memory`) - 本机内存 (依赖具体实现) 需要注意的是,默认行为并非等到完全耗尽才返回错误消息——只要检测到性能下降严重至不可接受程度即可提前终止进程以保护整体稳定性[^2]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值