一文深入理解Java内存区域与内存溢出异常

 更新时间:2025年11月05日 09:04:58   作者:钮祜禄.爱因斯晨  
在Java开发领域,内存溢出犹如一颗隐藏的定时炸弹,随时可能让程序崩溃,给用户带来糟糕的体验,这篇文章主要介绍了Java内存区域与内存溢出异常的相关资料,需要的朋友可以参考下

一、引言

在 Java 编程领域,内存管理看似由虚拟机自动操持,开发者无需过度介入。然而,当内存泄漏或溢出问题悄然浮现,若对虚拟机内存运作机制缺乏深入认知,排查与修复工作将举步维艰。本文将深入剖析 Java 内存区域,并对常见的内存溢出异常进行详细探讨。

二、Java 运行时数据区域

(一)程序计数器

  • 功能:程序计数器是一块较小的内存空间,其作用是指示当前线程执行字节码的行号,是程序控制流的关键指示器,负责分支、循环、跳转等流程控制。
  • 特性:为线程私有,当线程执行 Java 方法时,记录字节码指令地址;执行本地方法时,计数器值为空。该区域不会出现 OutOfMemoryError

(二)Java 虚拟机栈

  • 线程私有性:Java 虚拟机栈同样是线程私有的,其生命周期与线程紧密相连。
  • 栈帧结构:以栈帧为单位存储方法执行时的局部变量表、操作数栈、动态连接及方法出口等信息。局部变量表存放基本数据类型和对象引用,编译期确定其大小。
  • 异常情况:可能抛出 StackOverflowError(当线程请求的栈深度超过虚拟机允许的深度)和 OutOfMemoryError(栈动态扩展时无法申请到足够内存)。

(三)本地方法栈

  • 功能与虚拟机栈的关系:功能类似虚拟机栈,主要为本地方法执行提供支持。
  • 实现方式:实现方式由虚拟机自行决定。
  • 异常抛出:在栈深度溢出或扩展失败时,会抛出 StackOverflowErrorOutOfMemoryError

(四)Java 堆

  • 地位与作用:是虚拟机管理的最大内存区域,被所有线程共享,用于存放对象实例,是垃圾收集的主要区域。
  • 分代收集理论:基于分代收集理论进行区域划分,可设置为固定或扩展大小。
  • 内存溢出情况:当内存不足且无法扩展时,抛出 OutOfMemoryError

(五)方法区

  • 线程共享性:线程共享区域,用于存储类型信息、常量、静态变量等。
  • 历史变迁:JDK 8 前常被称为 “永久代”,之后采用元空间实现。
  • 异常情况:内存不足时抛出 OutOfMemoryError

(六)运行时常量池

  • 所属区域:属于方法区,存放编译期生成的字面量与符号引用。
  • 动态性:具备动态性,运行时可添加常量。
  • 内存异常:内存申请失败时抛出 OutOfMemoryError

(七)直接内存

  • 特殊性质:并非虚拟机规范定义区域,NIO 可利用其分配堆外内存以提升性能。
  • 内存限制:不受 Java 堆大小限制,但受本机内存制约。
  • 溢出异常:超出限制时抛出 OutOfMemoryError

三、内存溢出异常实战

(一)Java 堆溢出

示例代码:通过不断创建对象耗尽堆内存。

import java.util.ArrayList;
import java.util.List;
class HeapOOM {
    static class OOMObject {}
    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while (true) {
            list.add(new OOMObject());
        }
    }
}

解决思路:可通过 -Xmx 参数设置堆大小,溢出时抛出 OutOfMemoryError: Java heap space,可调整堆大小或优化对象创建逻辑来解决。

(二)虚拟机栈溢出

示例代码:利用无限递归使栈深度超限。

class StackSOF {
    private static void stackLeak() {
        stackLeak();
    }
    public static void main(String[] args) {
        try {
            stackLeak();
        } catch (Throwable e) {
            System.out.println("Stack depth: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

解决方法:抛出 StackOverflowError,需检查递归算法并设置终止条件。

(三)方法区和运行时常量池溢出

示例代码:持续向常量池添加字符串。

import java.util.ArrayList;
import java.util.List;
class MethodAreaOOM {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

应对策略:抛出 OutOfMemoryError,需关注常量池使用情况,避免无节制创建常量。

(四)本机直接内存溢出

示例代码:借助Unsafe类不断分配直接内存。

import sun.misc.Unsafe;
import java.lang.reflect.Field;
class DirectMemoryOOM {
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) throws IllegalAccessException {
        Field unsafeField = Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe = (Unsafe) unsafeField.get(null);
        while (true) {
            unsafe.allocateMemory(_1MB);
        }
    }
}

解决方案:抛出 OutOfMemoryError,需合理配置虚拟机参数并监控直接内存使用。

四、结语

{
unsafe.allocateMemory(_1MB);
}
}
}

**解决方案**:抛出 `OutOfMemoryError`,需合理配置虚拟机参数并监控直接内存使用。

深入掌握 Java 内存区域划分及内存溢出异常原理,是 `Java` 开发者进阶路上的关键。在日常开发中,应养成良好的内存管理习惯,借助工具监控内存使用,确保程序稳定、高效运行。

到此这篇关于Java内存区域与内存溢出异常的文章就介绍到这了,更多相关Java内存区域与内存溢出异常内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot如何优雅地处理全局异常详解

    SpringBoot如何优雅地处理全局异常详解

    这篇文章主要给大家介绍了关于SpringBoot如何优雅地处理全局异常的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用SpringBoot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • js-tab选项卡

    js-tab选项卡

    本文主要介绍了js-tab选项卡的示例代码。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • MyBatis实现MySQL批量插入的示例代码

    MyBatis实现MySQL批量插入的示例代码

    本文主要介绍了MyBatis实现MySQL批量插入的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • SpringBoot+Vue实现EasyPOI导入导出的方法详解

    SpringBoot+Vue实现EasyPOI导入导出的方法详解

    项目开发过程中,很大的需求都有 导入导出功能。本文将利用SpringBoot+Vue实现EasyPOI导入导出功能,感兴趣的可以了解一下
    2022-08-08
  • 如何把char数组转换成String

    如何把char数组转换成String

    这篇文章主要介绍了如何把char数组转换成String问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • 使用jdbcTemplate查询返回自定义对象集合代码示例

    使用jdbcTemplate查询返回自定义对象集合代码示例

    这篇文章主要介绍了使用jdbcTemplate查询返回自定义对象集合代码示例,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Mybatis 逆向工程的三种方法详解

    Mybatis 逆向工程的三种方法详解

    这篇文章主要介绍了Mybatis 逆向工程的三种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • Java实现数字金额转换为中文大写金额的完整指南与优化技巧

    Java实现数字金额转换为中文大写金额的完整指南与优化技巧

    在金融、财务和商务系统中,将数字金额转换为中文大写金额是一项常见需求,中文大写金额能有效避免篡改和歧义,是合同、发票等正式场景的规范要求,本文将通过Java实现这一功能,需要的朋友可以参考下
    2025-12-12
  • 关于使用Mybatisplus自带的selectById和insert方法时的一些问题

    关于使用Mybatisplus自带的selectById和insert方法时的一些问题

    这篇文章主要介绍了关于使用Mybatisplus自带的selectById和insert方法时的一些问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • SpringBoot实现监控Actuator,关闭redis监测

    SpringBoot实现监控Actuator,关闭redis监测

    这篇文章主要介绍了SpringBoot实现监控Actuator,关闭redis监测,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11

最新评论