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