面试题:简述 OOM 你遇到过哪些情况,SOF 你遇到过哪些情况 ?

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
点赞12 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容