1、java.lang.StackOverflowError
报这个错误一般是由于方法深层次的调用,默认的线程栈空间大小一般与具体的硬件平台有关。栈内存为线程私有的空间,每个线程都会创建私有的栈内存。栈空间内存设置过大,创建线程数量较多时会出现栈内存溢出StackOverflowError。同时,栈内存也决定方法调用的深度,栈内存过小则会导致方法调用的深度较小,如递归调用的次数较少。
Demo:
public class StackOverFlowErrorDemo { static int i = 0; public static void main(String[] args) { stackOverflowErrorTest(); } private static void stackOverflowErrorTest() { i++; System.out.println("这是第 "+i+" 次调用"); stackOverflowErrorTest(); } } //运行结果: 。。。。 这是第 6726 次调用 这是第 6727 次调用 Exception in thread "main" java.lang.StackOverflowError 。。。。
注意:这是一个Error!!!!
2、java.lang.OutOfMemoryError: Java heap space
Heap size 设置 JVM堆的设置是指:java程序执行过程中JVM能够调配使用的内存空间的设置。JVM在启动的时候会自己主动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。能够利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。
Demo:
public class OOMHeapSpaceDemo { public static void main(String[] args) { byte[] bytes = new byte[30*1024*1024]; } }
然后修改堆内存的初始容量和最大容量为5MB
运行程序,查看结果:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at jvm.OOMHeapSpaceDemo.main(OOMHeapSpaceDemo.java:7)
注意:这是一个Error!!!!
3、java.lang.OutOfMemoryError:GC overhead limit exceeded
GC回收时间过长时会抛出的OutOfMemory。过长是指,超过98%的时间都在用来做GC并且回收了不到2%的堆内存。连续多次的GC,都回收了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit 错误会发生什么事情呢?那就是GC清理出来的一点内存很快又会被再次填满,强迫GC再次执行,这样造成恶性循环,CPU的使用率一直很高,但是GC没有任何的进展。
Demo:
/** * 调整虚拟机的参数: * -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()); System.out.println(i); } } } //运行结果: [Full GC (Ergonomics) [PSYoungGen: 1024K->1024K(2048K)] [ParOldGen: 7101K->7099K(7168K)] 8125K->8123K(9216K), [Metaspace: 3264K->3264K(1056768K)], 0.0296282 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
可以看到GC前后内存占用时一样的,也就是说虽然在频繁的GC,但是并没有起到什么作用,并没有回收回来多少的内存。
4、java.lang.OutOfMemoryError:Direct buffer memory
写NIO程序经常使用到ByteBuffer来读取或者写入数据,这是一种基于通道与缓冲区的I/O方式。它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中提高性能,因为避免了java堆和Native堆中来回复制数据。
- ByteBuffer.allocate(capability) :这种方式是分配JVM堆内存,属于GC管辖范围之内。由于需要拷贝,所以速度相对较慢;
- ByteBuffer.allocateDirect(capability):这种方式是直接分配OS本地内存,不属于GC管辖范围之内,由于不需要内存拷贝所以速度相对较快。
但是如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象就不会被回收。这时候堆内存充足,但是本地内存已经用光了,再次尝试分配的时候就会出现OutOfMemoryError,那么程序就直接崩溃了。
Demo:
/** * JVM配置参数: * -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m */ public class DirectBufferMemoryDemo { public static void main(String[] args) { System.out.println("配置的MaxDirectMemorySize"+sun.misc.VM.maxDirectMemory()/(double)1024/1024+" MB"); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6*1024*1024); } } //运行结果: 配置的MaxDirectMemorySize5.0 MB [GC (System.gc()) [PSYoungGen: 1785K->488K(2560K)] 1785K->728K(9728K), 0.0019042 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (System.gc()) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 240K->640K(7168K)] 728K->640K(9728K), [Metaspace: 3230K->3230K(1056768K)], 0.0077924 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
5、java.lang.OutOfMemoryError:unable to create new native thread
准确的说,这一个异常是和程序运行的平台相关的。导致的原因:
- 创建了太多的线程,一个应用创建多个线程,超过系统承载极限;
- 服务器不允许应用程序创建这么多的线程,Linux系统默认的允许单个进程可以创建的线程数量是1024个,当创建多 线程数量多于这个数字的时候就会抛出此异常
如何解决呢?
- 想办法减少应用程序创建的线程的数量,分析应用是否真的需要创建这么多的线程。如果不是,改变代码将线程数量降到最低;
- 对于有的应用,确实需要创建很多的线程,远超过Linux限制的1024个 限制,那么可以通过修改Linux服务器的配置,扩大Linux的默认限制。
Demo:
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(); } } } //运行结果: 。。。。 i = 92916 i = 92917 Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread 。。。。
6、java.lang.OutOfMemoryError:MetaSpace
元空间的本质和永久代类似,都是对JVM规范中的方法区的实现。不过元空间与永久代之间最大的区别在于:元空间不在虚拟机中,而是使用的本地内存。因此,默认情况下,元空间的大小仅仅受到本地内存的限制 。
元空间存放了以下的内容:
- 虚拟机加载的类信息;
- 常量池;
- 静态变量;
- 即时编译后的代码
模拟MetaSpace空间溢出,我们不断生成类往元空间里灌,类占据的空间总是会超过MetaSpace指定的空间大小的
查看元空间的大小:java -XX:+PrintFlagsInitial
Demo:
/** * JVM参数配置: * -XX:MetaSapceSize=5m */ public class MetaSpaceDemo { public static void main(String[] args) { for (int i = 0; i < 100_000_000; i++) { generate("eu.plumbr.demo.Generated" + i); } } public static Class generate(String name) throws Exception { ClassPool pool = ClassPool.getDefault(); return pool.makeClass(name).toClass(); } }
来源:https://www.cnblogs.com/simon-1024/p/12221917.html