JAVA | 聚焦 OutOfMemoryError 异常

[复制链接]
发表于 2025-9-20 01:17:34 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
个人主页
文章专栏

  

在正文开始前,我想多说几句,也就是吐苦水吧…近来这段时间不停想写点东西,停下来反思思索一下。
心中万言,真正执笔时又不知先写些什么。通常这个时间,我都会随便写写,文风极像散文,形散意不散吧!
先说一下近况,近来到场了Mathorlab数学建模,作为一个大一的学生,第一次到场那么高强度的比赛。深深的意识到本身的不敷,天之大,不外蚍蜉撼树。我不外是渺小的九牛一毛。竟欲与彼苍比高,蚍蜉撼树、痴人说梦。梦醒了,照旧得加油干…高考后,灵活的以为本身不消学习了,厥后发现,本身一辈子都要学习。害,挺惆怅的,也挺无助的。
我,出如今这里。大概你们以为我是理科生,工科女…哦不!我江苏高考纯文身世,这像极了案底。高二下之前,我的确是根正苗红的物化生。惋惜,尔辈不能究物理。我无比痛恨当时的决定,但是人是不会满意的,那时间不管学啥,我都会痛恨。用如今的眼光,去抱怨当时的本身。这像极了霸凌…
我本科财政管理专业,但是我偏离了轨道。去学了盘算机…半夜梦回,总会觉着本身可笑。一个文科生,还不是很好的大学,去学了盘算机…很疯狂!看到这儿,你大概会笑话我…太蚍蜉撼树了!但是,哎!
其实,当我步入大学没多久我就意识到这个轨道的止境是什么。
假如你问我读书的意义是什么?上年这个时间,我大概还会说:为天地立心,为生民立命…多么正气,多么理想化!但是,如今我只想以后我有本事养活本身。
唉,我也不知道我的选择是不是正确的。我也不知道,我未来的我怎么去对待这个选择,越想越内耗。
我和我朋侪说,我考研要考南京邮电大学…我也不知道未来怎么样,这是19岁的我选择的最得当我的出路了。反正认准了就好好去积极吧。就算错了,重新再来就是了。我才不外19岁,我最大的上风就是年轻…
共勉吧!


在 Java 开辟中,内存溢出异常是影响步伐稳固性的关键题目。相识其原理和应对方法,对开辟者至关告急。
一、Java 堆溢出

原理

Java 堆用于存储对象实例。不断创建对象,且制止垃圾回收器回收,对象数目超出堆容量时,就会引发堆溢出。
示例代码

  1. // VM Args: -Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError
  2. public class HeapOOM {
  3.     static class OOMObject {}
  4.     public static void main(String[] args) {
  5.         List<OOMObject> list = new ArrayList<>();
  6.         while (true) {
  7.             list.add(new OOMObject());
  8.         }
  9.     }
  10. }
复制代码
办理思路



  • 利用内存映像分析工具(如 Eclipse Memory Analyzer )分析堆转储快照。
  • 区分内存走漏和内存溢出:若存在无用对象恒久占用内存,是内存走漏;若对象都有用但堆空间不敷,可调解堆参数(-Xmx 与 - Xms ),并优化代码淘汰内存占用。
二、虚拟机栈和本地方法栈溢出

原理



  • 线程哀求栈深度超虚拟机答应值,抛出StackOverflowError 。
  • 虚拟机栈若支持动态扩展,扩展时内存申请失败,抛出OutOfMemoryError (HotSpot 不支持栈动态扩展 )。
示例代码



  • 测试StackOverflowError
  1. // VM Args: -Xss128k
  2. public class JavaVMStackSOF {
  3.     private int stackLength = 1;
  4.     public void stackLeak() {
  5.         stackLength++;
  6.         stackLeak();
  7.     }
  8.     public static void main(String[] args) throws Throwable {
  9.         JavaVMStackSOF oom = new JavaVMStackSOF();
  10.         try {
  11.             oom.stackLeak();
  12.         } catch (Exception e) {
  13.             System.out.println("stack length:" + oom.stackLength);
  14.             throw e;
  15.         }
  16.     }
  17. }
复制代码


  • 测试大量线程导致内存溢出
  1. // VM Args: -Xss2M
  2. public class JavaVMStackOOM {
  3.     private void dontStop() {
  4.         while (true) {}
  5.     }
  6.     public void stackLeakByThread() {
  7.         while (true) {
  8.             Thread thread = new Thread(() -> dontStop());
  9.             thread.start();
  10.         }
  11.     }
  12.     public static void main(String[] args) throws Throwable {
  13.         JavaVMStackOOM oom = new JavaVMStackOOM();
  14.         oom.stackLeakByThread();
  15.     }
  16. }
复制代码
办理思路



  • 出现StackOverflowError 时,可根据错误堆栈分析递归调用等题目代码。
  • 对于大量线程导致的内存溢出,可淘汰线程数目、调解栈内存巨细(-Xss ),或升级 64 位虚拟机以获取更多内存。
三、方法区和运行时常量池溢出

原理



  • 方法区存储类信息、常量池等。运行时动态天生大量类(如利用 CGLib ),会耗尽方法区空间。
  • 运行时常量池是方法区一部分,字符串操纵(如String.intern() )不当,大概导致常量池溢出。
示例代码



  • 方法区溢出测试(借助 CGLib )
  1. // VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M
  2. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;
  5. public class JavaMethodAreaOOM {
  6.     static class OOMObject {}
  7.     public static void main(String[] args) {
  8.         while (true) {
  9.             Enhancer enhancer = new Enhancer();
  10.             enhancer.setSuperclass(OOMObject.class);
  11.             enhancer.setUseCache(false);
  12.             enhancer.setCallback(new MethodInterceptor() {
  13.                 @Override
  14.                 public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable {
  15.                     return proxy.invokeSuper(obj, args);
  16.                 }
  17.             });
  18.             enhancer.create();
  19.         }
  20.     }
  21. }
复制代码


  • 运行时常量池溢出测试(String.intern() )
  1. // JDK 6 运行:-XX:PermSize=6M -XX:MaxPermSize=6M
  2. // JDK 7及以上运行:-Xmx6M
  3. public class RuntimeConstantPoolOOM {
  4.     public static void main(String[] args) {
  5.         String str1 = new StringBuilder("计算机").append("软件").toString();
  6.         System.out.println(str1.intern() == str1);
  7.         String str2 = new StringBuilder("ja").append("va").toString();
  8.         System.out.println(str2.intern() == str2);
  9.     }
  10. }
复制代码
办理思路



  • 方法区溢出时,调解方法区相干参数(如 JDK 8 前的 - XXermSize 和 - XX:MaxPermSize ,JDK 8 及以后的 - XX:MetaspaceSize 等 ),优化代码淘汰动态类天生。
  • 针对常量池溢出,公道利用String.intern() 方法,制止偶然义的字符串入池操纵。
四、直接内存溢出

原理

直接内存容量由-XX:MaxDirectMemorySize 参数控制,默认与 Java 堆最大值类似。直接或间接利用DirectByteBuffer 、Unsafe 中分配内存超出限定,会引发溢出。
示例代码

  1. // VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M
  2. import sun.misc.Unsafe;
  3. import java.lang.reflect.Field;
  4. public class DirectMemoryOOM {
  5.     private static final int _1MB = 1024 * 1024;
  6.     public static void main(String[] args) throws Exception {
  7.         Field unsafeField = Unsafe.class.getDeclaredFields()[0];
  8.         unsafeField.setAccessible(true);
  9.         Unsafe unsafe = (Unsafe) unsafeField.get(null);
  10.         while (true) {
  11.             unsafe.allocateMemory(_1MB);
  12.         }
  13.     }
  14. }
复制代码
办理思路


  • 公道设置-XX:MaxDirectMemorySize 参数。
  • 排查代码中直接内存分配操纵,如 NIO 相干代码,确保内存分共同理。
ld.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
  1. 解决思路
  2. - 合理设置`-XX:MaxDirectMemorySize` 参数。
  3. - 排查代码中直接内存分配操作,如 NIO 相关代码,确保内存分配合理。
  4. 通过深入理解 Java 内存溢出异常原理,结合具体代码示例和解决思路,开发者能更好地定位和解决内存问题,保障 Java 程序稳定运行。
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表