java面试-谈谈你对OOM的理解

一、OOM(OutOfMemoryError):

对象无法释放或无法被垃圾回收,造成内存浪费,导致程序运行速度减慢,甚至系统崩溃等严重后果,就是内存泄漏。多个内存泄漏造成可使用内存变少,会导致内存溢出。

代码问题new 一个很大对象,导致内存溢出

也可能内存真的不足导致内存溢出。

二、考虑:

1、垃圾回收算法设置是否合理

2、年轻代、老年代划分是否合理

3、内存泄漏

三、典型情况:

1、java.lang.StackOverflowError

  • 在一个函数中调用自己就会产生这样的错误(栈溢出)
  • 发生区域:java虚拟机栈或本地方法栈
public class StackOverFlowErrorDemo {
    public static void main(String[] args) {
        stackOverFlowError();
    }

    public static void stackOverFlowError(){
        stackOverFlowError();
    }
}

2、java.lang.OutOfMemoryError: Java heap space

  • new 一个很大对象
  • 发生区域:java堆
/**
 * -Xms10m -Xmx10m
 */
public class JavaHeapSpaceDemo {

    static class  OOMObject{
    }

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
    }
}

3、java.lang.OutOfMemoryError: GC overhead limit exceeded

  • GC回收时间过长,超过98%的时间用来做GC,并且回收了不到2%的堆内存
/**
 * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
 */
public class GCoverheadDemo {

    public static void main(String[] args) {
        int i = 0;
        List<String> list = new ArrayList<>();

        while (true) {
            list.add(String.valueOf(++i).intern());
        }
    }
}  

4、java.lang.OutOfMemoryError: Direct buffer memory

原因:直接内存不足

写NIO程序经常使用ByteBuffer来读取或写入数据,这是一种基于通道与缓冲区的I/O方式

ByteBuffer.allocate() 分配JVM堆内存,属于GC管辖范围,需要拷贝所以速度相对较慢

ByteBuffer.allocateDirect() 分配操作系统本地内存,不属于GC管辖范围,不需要内存拷贝所以速度相对较快

/**
 * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
 */
public class DirectBufferMemoryDemo {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("maxDirectMemory : " + sun.misc.VM.maxDirectMemory() / 1024 / 1024 + "MB");
        TimeUnit.SECONDS.sleep(1);
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

5、java.lang.OutOfMemoryError : unable to create new native thread

  • 一个应用进程创建了多个线程,超过系统承载极限,Linux默认允许单个进程可以创建的线程数1024
/**
 * 一个应用进程创建了多个线程,超过系统承载极限,Linux默认是1024
 */
public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
        for (int i = 0; ; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}  

linux系统下:

ulimit -u  --查看线程上限
cat /etc/security/limits.d/20-nproc.conf 

6、java.lang.OutOfMemoryError: Metaspace

java8使用Metaspace代替永久代,与永久代最大的区别是:元空间并不在虚拟机内存中,而是使用本地内存。  

永久代(java8使用Metaspace)存放的信息:

  • 虚拟机加载的类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码
/**
 * 不断生成类往元空间推,类占据的空间总是会超过Metaspace的大小
 * -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
 */
public class MetaspaceOOMDemo {

    static class OOMTest {
    }

    public static void main(String[] args) {
        int i = 0; //模拟计数多少次后发生了异常
        try {
            while (true) {
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invokeSuper(o, args);
                    }
                });
                enhancer.create();
            }
        } catch (Throwable e) {
            System.out.println(i + "次后发送了异常");
            e.printStackTrace();
        }

    }
}

  

  

 

posted @ 2019-07-06 17:17  与君共舞  阅读(1316)  评论(0编辑  收藏  举报