image.png

一、java.lang.StackOverflowError(栈溢出)

栈的大小默认为512k—1024k,当进行递归调用,随着方法的深度加载,最终可能会把栈撑爆,发现错误StackOverflowError

1.1 Demo

  1. public class StackOverflowErrorDemo {
  2. public static void main(String[] args) {
  3. stackOverflowError();
  4. }
  5. private static void stackOverflowError(){
  6. stackOverflowError();
  7. }
  8. }

image.png

二、OutOfMemoryError: Java heap space(堆溢出)

2.1 Demo

设置初始堆内存和最大堆内存为10m
image.png

public class JavaHeapSpaceDemo {
    public static void main(String[] args) {
        //生成一个大对象
        byte[] bytes=new byte[20*1024*1024];
    }
}

image.png

三、java.lang.OutOfMemoryError: GC overhead limit exceeded(GC时间过高)

3.1 产生原因

GC回收时间过长时会抛出OutOfMemroyError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC 都只回收了不到2%的极端情况下才会抛出。加入不抛出GC overhead limit exceeded错误,nameGC清理的一点内存会再次填满,迫使GC再次执行,形成恶性循环。
CPU使用率一直是100%,而GC却没有任何成果。

3.2 解决方法

1、增加heap堆内存
2、一般出现这种问题跟内存其实没太大关系,主要还是代码问题,代码中出现了大量占用内存的对象,一定要检查代码,下面我们来代码演示。

3.3 Demo

演示JVM参数配置
-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

import java.util.ArrayList;
import java.util.List;

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;
        }
    }
}

image.png

四、java.lang.OutOfMemoryError: Direct buffer memory(直接内存溢出)

4.1 产生原因

NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
ByteBuffer.allocate(capability)是分配JVM堆内存,属于GC管辖范围,由于需要拷贝速度相对较慢。
ByteBuffer.allocteDirect(capability)是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。
但是如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,程序也就直接崩溃了。

4.2 Demo

VM配置
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

import java.nio.ByteBuffer;

public class DirectBufferMemoryDemo {
    public static void main(String[] args) {
        System.out.println("查看配置的本地内存maxDirectMemory:"+(sun.misc.VM.maxDirectMemory() / (double)1024 / 1024) +"MB");
        //停顿一下方便看效果
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //-XX:MaxDirectMemorySize=5m 本地内存配置的是5MB,这里实际使用的是6MB
        ByteBuffer bb = ByteBuffer.allocateDirect(6 * 1024 * 1024);

    }
}

image.png

五、java.lang.OutOfMemoryError:unable to create new native thread(无法创建新的本地线程)

5.1 产生原因

这个错误在高并发请求服务器经常会出现,因为这个native thread 异常与对应的平台有关。
原因:

  1. 你的应用创建太多线程了,一个应用进程创建多个线程,超过系统承载极限。
  2. 你的服务器并不允许你的应用程序创建这么多线程,linux系统默认允许单个进程可以创建的线程数是1024个,你的应用创建超过这个数量,就会报java.lang.OutOfMemoryError: unable to create new native thread异常。

    5.2 Demo

    在linux系统上进行演示.
    注意:代码一定不能上传到root用户,可以新建个用户来演示,因为关闭进程我们需求切换到root用户,如果在root用户则需要强制重启虚拟机。
    public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
      for (int i = 1;  ; i++) {
          System.out.println("=========================i:" + i);
          new Thread(()->{
              try {
                  Thread.sleep(Integer.MAX_VALUE);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          },""+i).start();
      }
    }
    }
    
    把java文件【UnableCreateNewThreadDemo.java】上传到linux系统上 然后编译
    javac -d . UnableCreateNewThreadDemo.java
    
    查看包名
    cat UnableCreateNewThreadDemo.java
    
    image.png
    运行 ```java java com.test.UnableCreateNewThreadDemo
![image.png](https://cdn.nlark.com/yuque/0/2021/png/23000517/1635646858450-7c2341d9-a443-4172-853d-e9cdd914fc95.png#clientId=u92ac947d-9b4e-4&from=paste&id=u88730d67&margin=%5Bobject%20Object%5D&name=image.png&originHeight=701&originWidth=1170&originalType=url&ratio=1&size=48559&status=done&style=none&taskId=u9fe8ec51-97d0-4a94-ae72-c2e340f4a57)<br />切换root账户 杀死进程
```java
ps -ef|grep java


kill -9 1425

image.png
切换到新建用户,这时我们显示已杀死,用Ctrl+c是退不出去的必须root用户强制杀死这个进程
image.png

5.3 查看最大线程数

ulimit -u

image.png

5.4 修改最大线程数

image.png

vim /etc/security/limits.d/20-nproc.conf

image.png
我们修改了5000 重启服务器,我们查看发现修改的已经从4096变为我们修改的5000

ulimit -u

image.png
这种修改治标不治本,其实导致这个java.lang.OutOfMemoryError: unable to create new native thread错误,还需要从我们代码入手来解决。

六、java.lang.OutOfMemoryError: Metaspace(元空间不足)

6.1 Metaspace

java8 及以后的版本使用Metaspace来代替永久代,Metaspace是方法区在HotSpot中的实现,它与持久代最大区别在于,Metaspace并不在虚拟机内存中而是使用本地内存也就是在JDK8中,classe metadata(the virtual machines internal presentation of Java class),被存储在叫做Metaspace的native memory.
永久代(java 8 后被元空间Metaspace取代了)存放了以下信息:

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

    6.2 出现问题原因

    错误的主要原因, 是加载到内存中的 class 数量太多或者体积太大。

    6.3 Demo

    模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间是会超过Metaspace指定的空间大小的
    1.查看元空间大小 java -XX:+PrintFlagsInitial
    image.png
    2.设置配置 这里设置10m方便演示效果
    -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

    3.代码 ```java import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MetaspaceDemo { static class OOM{} public static void main(String[] args) { int i = 0;//模拟计数多少次以后发生异常 try { while (true){ i++; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOM.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(); } } } ``` image.png