找到该死的OutOfMemoryError

本文介绍了解决Java应用程序中出现的OutOfMemoryError问题的方法。通过调整JVM参数并使用MAT工具分析.hprof文件来定位内存泄漏的原因。

看到下面这一堆信息, 我想你应该知道要做什么了.

main
  at java.lang.OutOfMemoryError.<init>()V (OutOfMemoryError.java:48)
  at java.util.Arrays.copyOfRange([CII)[C (Arrays.java:2694)
  at java.lang.String.<init>([CII)V (String.java:203)
  at java.lang.StringBuilder.toString()Ljava/lang/String; (StringBuilder.java:405)
  at com.mkk.utils.GuidGeneratorTest.outOfMemory()V (GuidGeneratorTest.java:25)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; (Method.java:601)


加大内存,加大内存, 内存溢出了, 于是我设置了

JAVA_OPTS=-server -Xms2048m -Xmx2048m -XX:PermSize=1024m -XX:MaxPermSize=512m -Xss512k


重启服务器, 没过多久又 OutOfMemoryError


是时候知道该死的OutOfMemoryError,


所以, 请给JAVA_OPTS添加 

 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/error
(注: 红色部分为服务器的一路径,当OutOfMemoryError是JVM信息会保存在该目录里)


再重启服务器,期待下一次的OutOfMemoryError吧.


是的, OutOfMemoryError又来了...


现在,去/home/error目录看看吧. 是否有类似java_pid6472.hprof的文件生成, 还很大(好几十或几百MB).


现在, 推荐去下载MAT(Memory Analyzer Tool)工具在看看是什么东东把内存都消耗完了(也可用JDK自带的jvisualvm.exe工具). 下载地址:

http://www.eclipse.org/mat/downloads.php


对于如何分析 .hprof文件找到该死的OutOfMemoryError出现在哪儿, 我推荐阅读这篇文章

http://www.blogjava.net/rosen/archive/2010/06/13/323522.html


最后, 找到了源头. 去修改代码吧.

....




`OutOfMemoryError` 是 Java 中一个 **严重的运行时错误(Error)**,属于 `java.lang.Error` 的子类。它表示 **JVM 没有足够的内存来分配一个对象**,通常发生在堆内存、元空间(Metaspace)或线程栈空间不足时。 --- ## 一、为什么会抛出 `OutOfMemoryError`? `OutOfMemoryError` 通常由以下几种情况引发: | 类型 | 描述 | |------|------| | `Java heap space` | 堆内存不足,无法创建新对象 | | `GC overhead limit exceeded` | JVM 花费太多时间进行垃圾回收,但回收的内存太少 | | `PermGen space` | 永久代空间不足(JDK 8 及之前) | | `Metaspace` | 元空间不足(JDK 8 及之后) | | `unable to create new native thread` | 无法创建新线程,通常是系统资源耗尽 | | `Direct buffer memory` | 堆外内存(Direct Buffer)不足 | | `array size too big` | 请求分配的数组大小超过 JVM 限制 | --- ## 二、示例代码 ### 示例 1:堆内存溢出(Java heap space) ```java import java.util.ArrayList; import java.util.List; public class HeapOOM { public static void main(String[] args) { List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 每次分配 1MB } } } ``` #### 输出(可能): ``` Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at HeapOOM.main(HeapOOM.java:8) ``` --- ### 示例 2:元空间溢出(Metaspace) ```java import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class MetaspaceOOM { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Object.class); enhancer.setUseCache(false); enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> proxy.invokeSuper(obj, args1)); enhancer.create(); } } } ``` #### 输出(可能): ``` Exception in thread "main" java.lang.OutOfMemoryError: Metaspace at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:...) ``` --- ### 示例 3:无法创建新线程 ```java public class ThreadOOM { public static void main(String[] args) { while (true) { new Thread(() -> { try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } } } ``` #### 输出(可能): ``` Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:...) ``` --- ## 三、如何处理 `OutOfMemoryError`? ### ✅ 方法一:增加 JVM 内存参数 ```bash # 增加堆内存 java -Xms512m -Xmx2g MyApplication # 增加元空间大小 java -XX:MaxMetaspaceSize=512m MyApplication # 增加线程栈大小(默认 1MB) java -Xss2m MyApplication ``` ### ✅ 方法二:分析内存快照(heap dump) 使用以下参数生成内存快照: ```bash java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof MyApplication ``` 使用工具如 **Eclipse MAT(Memory Analyzer)** 或 **VisualVM** 打开 `.hprof` 文件,分析内存泄漏或内存使用情况。 ### ✅ 方法三:优化代码逻辑 - 避免内存泄漏(如缓存未清理、监听器未注销) - 避免无限增长的数据结构(如 `List`、`Map` 等) - 控制线程池大小,避免无限制创建线程 - 使用软引用(SoftReference)或弱引用(WeakReference)管理缓存 --- ## 四、常见 JVM 参数 | 参数 | 描述 | |------|------| | `-Xms<size>` | 初始堆大小 | | `-Xmx<size>` | 最大堆大小 | | `-XX:MaxMetaspaceSize=<size>` | 设置最大元空间大小 | | `-XX:+HeapDumpOnOutOfMemoryError` | 内存溢出时生成 heap dump 文件 | | `-XX:HeapDumpPath=<file>` | 指定 heap dump 文件路径 | | `-Xss<size>` | 设置线程栈大小 | --- ## 五、`OutOfMemoryError` 与 `StackOverflowError` 的区别 | 项目 | `OutOfMemoryError` | `StackOverflowError` | |------|---------------------|------------------------| | 类型 | 堆、元空间等内存不足 | 线程栈溢出(递归调用过深) | | 原因 | 分配对象失败 | 方法调用层数过多 | | 是否可恢复 | 通常不可恢复 | 可通过代码优化避免 | --- ## 六、总结 | 项目 | 描述 | |------|------| | 异常类型 | `java.lang.OutOfMemoryError` | | 继承关系 | `Error` → `VirtualMachineError` → `OutOfMemoryError` | | 抛出原因 | JVM 无法为新对象分配足够的内存 | | 如何避免 | 增加内存、分析 heap dump、优化代码逻辑 | | 常见搭配 | 与 `-XX:+HeapDumpOnOutOfMemoryError`、MAT、VisualVM 配合使用 | --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值