OutOfMemory (OOM)的类型与检测

本文深入探讨Java中的几种常见内存溢出情况,包括Java堆溢出、虚拟机栈和本地方法溢出、方法区及运行时常量池溢出,并介绍了如何通过设置虚拟机参数来观察这些现象。

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

Java堆溢出

Java堆存的是对象的实例,所以无限添加对象实例很容易造成堆溢出

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

添加虚拟机参数
-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/jj/Desktop
a)Xms20m 堆最小值
b)Xmx20m 堆最大值
c)HeapDumpOnOutOfMemoryError 内存堆转储快照
d)XX:HeapDumpPath=/Users/jj/Desktop 快照存放地址

结果:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /Users/jj/Desktop/java_pid584.hprof ...
Heap dump file created [27568513 bytes in 0.137 secs

使用MAT内存分析工具分析生成的快照可以看到
这里写图片描述
Shallow Heap是指对象本身占有的内存大小。
Retained Heap是指对象本身加上它引用的对象的大小。
这里OOMObject 的Shadow Heap和Retained Head相同的原因是OOMObject没有引用;而他是16的原因是对象头在64位机器上占有64bit,也就是16个字节,但是由于虚拟机自动使用压缩指针,所以占有12个字节,但是又由于对象占有的内存必须为8的倍数,所以最后结果是16字节。
字节长度计算的方式是 12+类型长度和->最接近8倍数的数字->结果

虚拟机栈和本地方法溢出

栈内存太小和已使用的栈空间太多都会导致虚拟机栈或本地方法溢出。

a)栈溢出

public class JavaVMSStack {
    private int stackLength =1 ;

    public void JavaVMSStack() {
        stackLength ++ ;
        JavaVMSStack();
    }
    public static void main(String[] args) {
        JavaVMSStack j = new JavaVMSStack();
        try {
            j.JavaVMSStack();
        } catch (Exception e) {
            System.out.println(j.stackLength);
            throw e;
        }
    }
}

虚拟机参数
-Xss160k

这里写图片描述

b)内存溢出

我们知道,虚拟机会为每个线程的栈先分配一段可以调整的内存,每个线程分配到的栈帧越大,可以建立的线程就越少,建立线程时就越容易把剩下的内存耗尽。


public class JavaVMSStackOOM {
    private void dontStop() {
        while (true) {
        }
    }

    public void stackLeakByThread() {
        while (true) {
            Thread thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    dontStop();
                }
            });
            thread.start();
        }

    }


    public static void main(String[] args) {
        JavaVMSStackOOM oom = new JavaVMSStackOOM();
        oom.stackLeakByThread();
    }
}

虚拟机参数:

-vss20M

在Windows平台的虚拟机中,Java的线程是映射到虚拟机的内核上的,隐私上述代码执行时有较大的风险,可能会导致系统假死。

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

运行时常量池是方法区的一部分。
在jdk1.6以前,由于常量池位于永久代中,而我们可以通过参数去限制方法区的大小,所以间接影响到常量池的容量。
而jdk1.7以后, 已经把原本放在永久代的字符串常量池移出, 放在Java堆中(元空间Metaspace)中,元数据并不在虚拟机中,使用的是本地的内存。

可以通过以下参数来指定元空间的大小:
  
  -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。

 -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。

### 关于内存不足异常的原因及解决方案 #### 原因分析 内存不足异常通常发生在程序试图分配超出可用物理或虚拟内存资源的情况下。以下是可能导致此类问题的一些常见原因: 1. **指针指向已释放的内存** 如果程序中的某个指针仍然指向已被删除的内存区域,则可能会引发未定义行为,甚至导致崩溃[^1]。 2. **位图创建失败** 在某些情况下,尝试通过 `CreateBitmap` 函数或其他图像处理操作来分配大量内存时,如果系统无法提供足够的连续内存空间,就会抛出内存不足错误[^2]。 3. **数据库进程内存耗尽** 数据库运行过程中可能因为 PGA(Program Global Area)堆栈溢出或者 REDO 缓冲区分配失败而触发内存不足错误。具体表现为 Oracle 报错 ORA-04030,这通常是由于内部缓冲区管理不当引起的[^3]。 4. **EDA 工具合成过程中的内存消耗过高** 使用像 Vivado 这样的 EDA 软件进行设计综合时,复杂的电路模型会占用大量的计算资源。当工具检测到剩余内存不足以完成任务时,它可能会突然终止并显示分段错误消息[^4]。 5. **Java 应用程序启动参数配置不合理** 对于基于 Java 的应用服务器如 WebLogic,在执行更新补丁等高负载操作期间如果没有适当调整 JVM 的最大堆大小限制 (-Xmx),就容易遇到 java.lang.OutOfMemory 错误[^5]。 #### 解决方案建议 针对上述提到的各种场景下的 OOM 问题,可以采取以下措施加以缓解: 1. **调试监控工具的应用** - 当怀疑存在非法访问被释放对象的情况时,应借助专业的静态代码审查器以及动态跟踪诊断平台找出根本原因,并修正逻辑漏洞。 2. **优化图片加载策略** - 避免一次性请求过大的图形数据结构;考虑采用渐进式下载技术或将大尺寸素材分割成更易于管理的小块再逐片渲染出来。 3. **增强数据库资源配置** - 定期清理不再使用的临时文件和缓存记录; - 根据实际工作负荷重新规划 SGA 和 PGA 参数设置以提高整体吞吐量效率; - 升级硬件设施扩充 RAM 总容量作为长远之计。 4. **改进 FPGA 设计流程** - 将大型项目拆分为若干子模块分别编译链接后再组合起来减少单次运算需求峰值; - 利用增量式构建模式加快迭代速度同时降低每次全量重算带来的额外负担。 5. **合理调配 JAVA 程序环境变量** - 修改启动脚本增加初始最小值(-Xms)至接近目标上限水平从而避免频繁GC动作干扰正常业务运转; - 结合实际情况测试验证最佳性能区间内的各项数值范围以便长期稳定运营。 ```bash java -Xms512m -Xmx2g MyApplication ``` 以上就是对于不同类型 Out Of Memory Exception 及其对应处置办法的一个概括总结。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值