1. OOM(OutOfMemoryError)
OOM 是 Java 中一种严重的错误,表示 JVM 内存不足,无法分配更多资源。常见的 OOM 情况包括:
1.1 堆内存溢出(Heap Space OOM)
- 原因: 堆内存中对象过多,超出了 JVM 配置的最大堆内存(
-Xmx
)。 - 常见场景:
- 大量对象未释放(内存泄漏)。
- 缓存数据过多(如未设置过期时间的缓存)。
- 大文件或大数据集加载到内存中。
- 示例代码:
List<byte[]> list = new ArrayList<>();while (true) {list.add(new byte[1024 * 1024]); // 不断分配 1MB 的数组}List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 不断分配 1MB 的数组 }List<byte[]> list = new ArrayList<>(); while (true) { list.add(new byte[1024 * 1024]); // 不断分配 1MB 的数组 }
- 解决方法:
- 增加堆内存大小(
-Xmx
)。 - 检查代码是否存在内存泄漏。
- 使用弱引用或软引用管理缓存。
- 增加堆内存大小(
1.2 方法区(元空间)溢出(Metaspace OOM)
- 原因: 方法区(Java 8 后称为元空间)存储类元数据,如果加载的类过多,可能导致 OOM。
- 常见场景:
- 动态生成大量类(如使用 CGLib 或 ASM)。
- 未限制类的加载数量。
- 示例代码:
public class MetaspaceOOM {static class OOMObject {}public static void main(String[] args) {while (true) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.create(); // 动态生成类}}}public class MetaspaceOOM { static class OOMObject {} public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.create(); // 动态生成类 } } }public class MetaspaceOOM { static class OOMObject {} public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.create(); // 动态生成类 } } }
- 解决方法:
- 增加元空间大小(
-XX:MaxMetaspaceSize
)。 - 减少动态类的生成。
- 增加元空间大小(
1.3 直接内存溢出(Direct Memory OOM)
- 原因: 直接内存(NIO 使用的堆外内存)超出 JVM 配置的限制。
- 常见场景:
- 使用 NIO 时分配过多直接内存。
- 未正确释放直接内存。
- 示例代码:
List<ByteBuffer> list = new ArrayList<>();while (true) {list.add(ByteBuffer.allocateDirect(1024 * 1024)); // 分配 1MB 直接内存}List<ByteBuffer> list = new ArrayList<>(); while (true) { list.add(ByteBuffer.allocateDirect(1024 * 1024)); // 分配 1MB 直接内存 }List<ByteBuffer> list = new ArrayList<>(); while (true) { list.add(ByteBuffer.allocateDirect(1024 * 1024)); // 分配 1MB 直接内存 }
- 解决方法:
- 增加直接内存大小(
-XX:MaxDirectMemorySize
)。 - 确保正确释放直接内存。
- 增加直接内存大小(
2. SOF(StackOverflowError)
SOF 表示栈溢出错误,通常是由于方法调用层次过深或递归调用未终止导致的。
2.1 递归调用未终止
- 原因: 递归调用没有终止条件,导致栈帧过多。
- 常见场景:
- 递归算法未设置终止条件。
- 递归深度过大。
- 示例代码:
public class StackOverflowExample {public static void recursiveMethod() {recursiveMethod(); // 无限递归}public static void main(String[] args) {recursiveMethod();}}public class StackOverflowExample { public static void recursiveMethod() { recursiveMethod(); // 无限递归 } public static void main(String[] args) { recursiveMethod(); } }public class StackOverflowExample { public static void recursiveMethod() { recursiveMethod(); // 无限递归 } public static void main(String[] args) { recursiveMethod(); } }
- 解决方法:
- 检查递归终止条件。
- 使用循环替代递归。
2.2 方法调用层次过深
- 原因: 方法调用链过长,导致栈帧过多。
- 常见场景:
- 复杂的业务逻辑调用链。
- 循环依赖调用。
- 示例代码:
public class DeepCall {public static void methodA() {methodB();}public static void methodB() {methodA();}public static void main(String[] args) {methodA();}}public class DeepCall { public static void methodA() { methodB(); } public static void methodB() { methodA(); } public static void main(String[] args) { methodA(); } }public class DeepCall { public static void methodA() { methodB(); } public static void methodB() { methodA(); } public static void main(String[] args) { methodA(); } }
- 解决方法:
- 优化调用链,减少方法嵌套。
- 增加栈大小(
-Xss
)。
3. 实际开发中遇到的场景
3.1 OOM 场景
- 场景 1: 缓存未设置过期时间,导致缓存数据过多,最终堆内存溢出。
- 场景 2: 使用 NIO 时,未正确释放直接内存,导致直接内存溢出。
- 场景 3: 动态生成大量类(如使用反射或字节码技术),导致元空间溢出。
3.2 SOF 场景
- 场景 1: 递归算法未设置终止条件,导致栈溢出。
- 场景 2: 复杂的业务逻辑调用链过长,导致栈帧过多。
结论
- OOM 通常与内存分配有关,包括堆内存、元空间和直接内存。
- SOF 通常与方法调用层次或递归调用有关。
- 在实际开发中,需要根据具体场景优化代码,合理配置 JVM 参数,避免 OOM 和 SOF 的发生。
THE END
暂无评论内容