设置Java最大堆大小后,无限循环创建对象的程序为何未抛出异常?
我来帮你拆解几个核心原因,结合你的代码逐一分析:
1. 你的catch块根本抓不到OutOfMemoryError
这是最容易踩的坑!OutOfMemoryError属于Error的子类,而你的代码里catch(Exception ex)只能捕获Exception及其子类,完全覆盖不到Error。就算JVM真的触发了内存不足错误,你的程序也不会进入这个catch块打印提示,而是会直接终止(可能你误以为程序还在正常运行,没注意到它已经崩溃了)。
如果想捕获OOM,你需要调整捕获范围:
catch (OutOfMemoryError e) { System.out.println("内存不足错误发生了"); e.printStackTrace(); }
或者直接捕获所有可抛出的类型:
catch (Throwable t) { System.out.println("发生了异常/错误"); t.printStackTrace(); }
2. 垃圾回收一直在默默清理无用对象
看你的循环逻辑:
for(;;){ A a = new A(); count++; // ... }
每次循环创建的A对象,在当前迭代结束后就没有任何引用指向它了——下一次循环会把a变量重新指向新的对象。这些不可达的对象会被JVM的垃圾回收器(GC)标记为垃圾,在合适的时机被回收掉。
也就是说,你一直在创建新对象,但GC一直在帮你清理旧的、没用的对象,堆内存不会被持续占满,自然不会触发OOM。
3. generateRandom()可能没产生新的字符串对象
你的A类里有多个String字段,依赖generateRandom()生成内容。如果这个方法返回的是字符串常量池里的对象(比如返回固定字符串,或者用String.intern()处理过),那所有A实例的这些String字段都会指向同一个内存对象,不会每次都占用新内存。
比如如果generateRandom()是这样写的:
private static String generateRandom() { return "固定字符串"; }
那每个A实例的str、str1等都是指向同一个字符串,内存消耗极低,就算创建再多A对象,堆内存也不会快速耗尽。
4. JVM堆内存设置可能过大
如果你设置的最大堆内存(比如-Xmx参数)远大于系统可用内存,或者系统还有大量空闲内存,那JVM可能需要运行极久才会达到内存上限——甚至永远不会,因为GC一直在回收旧对象。
内容的提问来源于stack exchange,提问作者Shrikey

