Java内存区域详解(重点) @ Lin | 2024-08-11T14:21:26+08:00 | 4 分钟阅读 | 更新于 2024-08-12T14:21:26+08:00

Java 内存区域详解(基于 HotSpot 虚拟机)

一、运行时数据区概述

Java 虚拟机(JVM)将内存划分为不同的数据区域,用于管理程序运行时的内存分配。根据 JDK 1.8 的规范,内存区域分为以下部分:

线程私有 线程共享 特殊区域
程序计数器 堆(Heap) 直接内存
虚拟机栈(Java 栈) 方法区(元空间)
本地方法栈

二、核心内存区域详解

1. 程序计数器(Program Counter Register)

作用:记录当前线程执行的字节码指令地址(行号指示器),用于分支、循环、跳转、异常处理和线程恢复。 • 特点: • 唯一不会出现 OutOfMemoryError 的区域。 • 生命周期与线程绑定,线程私有。

2. Java 虚拟机栈(Java Stack)

作用:存储方法调用的栈帧,包括局部变量表、操作数栈、动态链接、方法返回地址。 • 可能错误: • StackOverflowError:请求栈深度超过虚拟机允许的最大深度(如无限递归)。 • OutOfMemoryError:栈动态扩展时无法申请足够内存(较少见)。

3. 本地方法栈(Native Method Stack)

作用:为 JVM 调用 Native 方法服务(如 C/C++ 实现的方法)。 • 特点:在 HotSpot 中与虚拟机栈合并实现。 • 错误类型:与虚拟机栈相同(StackOverflowErrorOutOfMemoryError)。

4. 堆(Heap)

作用:存放对象实例和数组(几乎所有对象在此分配)。 • 结构(分代垃圾回收): • 新生代:Eden 区、Survivor 区(S0/S1)。 • 老年代:长期存活的对象(年龄阈值默认 15,通过 -XX:MaxTenuringThreshold 调整)。 • 关键问题: • OutOfMemoryError: Java heap space:堆内存不足。 • OutOfMemoryError: GC Overhead Limit Exceeded:垃圾回收效率极低。

5. 方法区(Method Area)

作用:存储类信息(类名、方法、字段)、常量、静态变量、即时编译器编译后的代码。 • 实现变化: • JDK 1.7 及之前:永久代(PermGen),受 JVM 内存限制。 • JDK 1.8 及之后:元空间(Metaspace),使用本地内存,默认无上限(受物理内存限制)。 • 错误OutOfMemoryError: Metaspace(元空间不足)。

6. 运行时常量池(Runtime Constant Pool)

作用:存放编译期生成的字面量(如字符串)和符号引用(类、方法、字段的引用)。 • 位置:方法区的一部分。

7. 直接内存(Direct Memory)

作用:通过 DirectByteBuffer 分配的堆外内存(如 NIO 操作),避免 Java 堆与 Native 堆的数据复制。 • 特点: • 不属于 JVM 运行时数据区,但可能导致 OutOfMemoryError。 • 容量受物理内存和处理器寻址空间限制。


三、对象创建过程(五步)

  1. 类加载检查

    JVM 检查 new 指令对应的类是否已加载、解析和初始化。若未加载,先执行类加载过程。

  2. 分配内存

    方式: ◦ 指针碰撞:堆内存规整时使用(如 Serial、ParNew 收集器)。 ◦ 空闲列表:堆内存不规整时使用(如 CMS 收集器)。 • 并发问题:通过 CAS + 失败重试TLAB(线程本地分配缓冲区) 解决。

  3. 初始化零值

    将分配的内存空间初始化为零值(如 int 初始化为 0,引用初始化为 null)。

  4. 设置对象头

    存储对象元数据:哈希码、GC 分代年龄、锁状态、类元数据指针等。

  5. 执行 <init> 方法

    按程序员编写的代码初始化对象(构造函数赋值等)。


四、对象访问定位

  1. 句柄方式

    实现:堆中划分句柄池,reference 存储句柄地址,句柄包含对象实例数据和类元数据地址。 • 优点:对象移动时只需更新句柄,reference 无需修改。

  2. 直接指针(HotSpot 默认)

    实现:reference 直接存储对象地址,对象头中包含类元数据指针。 • 优点:访问速度快(减少一次指针跳转)。


五、常见问题与调优

  1. 字符串常量池位置

    JDK 1.7 之前:永久代。 • JDK 1.7 及之后:堆中。避免永久代 GC 效率低的问题。

  2. 堆内存参数

    -Xms:初始堆大小。 • -Xmx:最大堆大小。 • -XX:NewRatio:新生代与老年代比例。

  3. 元空间参数

    -XX:MetaspaceSize:初始大小。 • -XX:MaxMetaspaceSize:最大大小(默认无限制)。


六、总结

线程私有区域(程序计数器、栈):生命周期与线程绑定,无垃圾回收。 • 线程共享区域(堆、方法区):需关注内存分配与回收策略。 • 直接内存:提升 I/O 性能,但需手动管理或依赖 JVM 参数限制。

理解 JVM 内存模型是排查内存溢出(OOM)、优化程序性能的基础,尤其在处理高并发、大数据场景时至关重要。

© 2019 - 2025 Lin 的博客

Powered by Hugo with theme Dream.

avatar
关于我

Lin 的 ❤️ 博客

记录一些 🌈 生活上,技术上的事

职业是JAVA全栈工程师