谈谈你对OOM的认识

java.lang.StackOverflowError

导致原因:方法调用特别多,把栈空间撑爆了。

public class StackOverflowErrorDemo { public static void main(String[] args) { stackOverflowError(); } private static void stackOverflowError() { stackOverflowError(); // java.lang.StackOverflowError } }

java.lang.OutOfMemoryError: Java heap space

导致原因:对象太多或太大,堆内存不够用了。

JVM参数配置:-Xms10m -Xmx10m

public class JavaHeapSpaceDemo {
    public static void main(String[] args) {
        String str = "atguigu";
        while(true){
            str += str + new Random().nextInt(111111111) + new Random().nextInt(222222222) ;
            str.intern();
        }
    }
}

java.lang.OutOfMemoryError: GC overhead limit exceeded

GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。连续多次GC都回收了不到2%的情况下才会抛出。

假如不抛出GC overhead limit 错误会发生什么情况呢?

那就是GC清理的这么点内存很快会再次填满,迫使GC再次执行,这样就形成恶性循环,CPU使用率一直是100%,而GC却没有任何成果。

JVM参数配置: -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m

public class GCOverheadDemo {
    public static void main(String[] args) {
        int i = 0;
        List<String> list = new ArrayList<>();
        try{
            while (true){
                list.add(String.valueOf(++i).intern());
            }
        }catch (Throwable e){
            System.out.println("************i: " + i);
            e.printStackTrace();
            throw e;
        }
    }
}

java.lang.OutOfMemoryError: Direct buffer memory

JVM参数配置:  -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

导致原因:写NIO程序经常使用ByteBuffer来读取或者写入数据,只是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免的在Java堆和Native堆中来回复制数据。

ByteBuffer.allocate(capability)第一种方式是分配JVM内存,数据GC管辖范围,由于需要拷贝所以速度相对较慢。

ByteBuffer.allocateDirect(capability)第二种方式是分配OS本地内存,不属于管辖范围,由于不需要内存拷贝所以速度较快。

但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。

这时候内存充足,但本地内存可能已经使用光了,再尝试分配本地内存就会出现OutOfMemoryError,那程序就直接崩了。

public class DirectBufferMemoryDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("配置的maxDirectMemory:" + (sun.misc.VM.maxDirectMemory() / (double)1024 / 1024) + "MB");
        Thread.sleep(3000);
        ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

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

导致原因:

1、你的应用创建了太多线程了,一个应用进程创建了多个线程,超过系统承载极限。

2、你的服务器并不允许你的应用程序创建这么多线程,Linux系统默认允许单个进程可以创建的线程数是1024个。你创建的线程超过这个数量,就会报java.lang.OutOfMemoryError: unable to create new native thread

解决办法:

1、想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,该代码将线程数量降到最低。

2、对于有的应用,确实需要创建很多线程,远超过linux系统的默认1024个线程的限制,可以通过修改linux服务器配置,扩大linux默认限制。

public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
        for (int i = 0; ; i++) {
            System.out.println("***************i:" + i);
            new Thread(() -> {
                try{
                    Thread.sleep(Integer.MAX_VALUE);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }, ""+i).start();
        }
    }
}

在linux系统上测试:java com.study.oom.UnableCreateNewThreadDemo.java

关闭: 

ps -ef | grep java

kill -9 18198

ulimit -u  // 查看线程上限

vim /etc/security/limits.d/90-nproc.conf // 修改线程上限(在90-nproc.conf中修改)

java.lang.OutOfMemoryError: Metaspace

元空间在方法区。

使用 java -XX:PrintFlagsInitial 命令查看本机的初始参数,

JVM参数配置:-XX:MetaspaceSize=8m

Java8以后的版本使用Metaspace来替代永久代。

Metaspace是方法区在HotSpot中的实现,它与永久代最大的区别在于:MetaSpace并不在虚拟机的内存中而是使用本地内存,也既在java8中,classe metadata(the virtual machines internal presentation of java class),被存储在叫做Metaspace的native memory

永久代(java8后被元空间Metaspace取代了)存放以下信息:

虚拟机加载的类信息、常量池、静态变量、即时编译后的代码

模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的。

public class MetaspaceOOMTest {
    static  class  OOMTest{

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值