- 一、java.lang.StackOverflowError(栈溢出)
- 二、OutOfMemoryError: Java heap space(堆溢出)
- 三、java.lang.OutOfMemoryError: GC overhead limit exceeded(GC时间过高)
- 四、java.lang.OutOfMemoryError: Direct buffer memory(直接内存溢出)
- 五、java.lang.OutOfMemoryError:unable to create new native thread(无法创建新的本地线程)
- 六、java.lang.OutOfMemoryError: Metaspace(元空间不足)
一、java.lang.StackOverflowError(栈溢出)
栈的大小默认为512k—1024k,当进行递归调用,随着方法的深度加载,最终可能会把栈撑爆,发现错误StackOverflowError。
1.1 Demo
public class StackOverflowErrorDemo {
public static void main(String[] args) {
stackOverflowError();
}
private static void stackOverflowError(){
stackOverflowError();
}
}
二、OutOfMemoryError: Java heap space(堆溢出)
2.1 Demo
设置初始堆内存和最大堆内存为10m
public class JavaHeapSpaceDemo {
public static void main(String[] args) {
//生成一个大对象
byte[] bytes=new byte[20*1024*1024];
}
}
三、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;
}
}
}
四、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);
}
}
五、java.lang.OutOfMemoryError:unable to create new native thread(无法创建新的本地线程)
5.1 产生原因
这个错误在高并发请求服务器经常会出现,因为这个native thread 异常与对应的平台有关。
原因:
- 你的应用创建太多线程了,一个应用进程创建多个线程,超过系统承载极限。
- 你的服务器并不允许你的应用程序创建这么多线程,linux系统默认允许单个进程可以创建的线程数是1024个,你的应用创建超过这个数量,就会报java.lang.OutOfMemoryError: unable to create new native thread异常。
5.2 Demo
在linux系统上进行演示.
注意:代码一定不能上传到root用户,可以新建个用户来演示,因为关闭进程我们需求切换到root用户,如果在root用户则需要强制重启虚拟机。
把java文件【UnableCreateNewThreadDemo.java】上传到linux系统上 然后编译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(); } } }
查看包名javac -d . UnableCreateNewThreadDemo.java
cat UnableCreateNewThreadDemo.java
运行 ```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
切换到新建用户,这时我们显示已杀死,用Ctrl+c是退不出去的必须root用户强制杀死这个进程
5.3 查看最大线程数
ulimit -u
5.4 修改最大线程数
vim /etc/security/limits.d/20-nproc.conf
我们修改了5000 重启服务器,我们查看发现修改的已经从4096变为我们修改的5000
ulimit -u
这种修改治标不治本,其实导致这个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
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(); } } } ```