java.lang.StackOverflowError
栈溢出错误
,这个错误很容易模拟,且看下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) { new StackOverflowTest().test(); } private static int high = 0 ;private void test () { try { ++high; test(); } finally { System.out.println("栈的深度为: " + high); } } --- JVM ARGS: -server -Xmn2m -Xss1m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:-UseTLAB
释
我们都知道方法的调用是通过入栈和计算出栈来实现的,所以我们在方法递归调用一定次数时,必然会发生栈溢出,栈溢出后,程序自动停止,是一类不可捕获和恢复的Error类型的错误,所以我们在使用递归算法时,应当注意递归的深度,防止出现栈溢出错误导致服务错误
java.lang.OutOfMemoryError:java heap space
JVM堆空间不足引起的内存溢出错误
,这类错误比较常见,此处就不做太多的解释,出现这类错误,你就去看GC日志,看看新生代、老年代、永久代/Metaspace的使用情况,如果是想查看GC的情况,使用如下JVM指令:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:./gclog.log
,gc的所有信息都会输出到gclog.log文件中
gclog.log
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 *JVM信息* Java HotSpot (TM) 64-Bit Server VM (25.161 -b12) for bsd-amd64 JRE (1.8 .0 _161-b12) , built on Dec 19 2017 16:22:20 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658 ) (LLVM build 2336.11 .00 ) Memory: 4k page, physical 16777216k (2991720 k free) /proc/meminfo: *JVM ARGS* CommandLine flags: -XX:+DoEscapeAnalysis -XX:+EliminateAllocations -XX:InitialHeapSize =268435456 -XX:MaxHeapSize=4294967296 -XX:MaxNewSize=2097152 -XX:NewSize=2097152 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:ThreadStackSize=1024 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC -XX:-UseTLAB *GC日志信息* 0.125 : [GC (Allocation Failure) [PSYoungGen: 1023 K->512 K(1536 K)] 1023 K->536 K(261632 K), 0.0010704 secs] [Times: user=0.01 sys=0.00 , real=0.00 secs] 0.157 : [GC (Allocation Failure) [PSYoungGen: 1535 K->493 K(1536 K)] 1559 K->847 K(261632 K), 0.0010655 secs] [Times: user=0.01 sys=0.00 , real=0.00 secs] ... 0.360 : [GC (Allocation Failure) [PSYoungGen: 1247 K->256 K(1536 K)] 2614 K->1727 K(261632 K), 0.0008285 secs] [Times: user=0.00 sys=0.00 , real=0.00 secs] Heap *年轻代* PSYoungGen total 1536 K, used 396 K [0x00000007bfe00000 , 0x00000007c0000000 , 0x00000007c0000000 ) eden space 1024 K, 13 % used [0x00000007bfe00000 ,0x00000007bfe23268 ,0x00000007bff00000 ) from space 512 K, 50 % used [0x00000007bff00000 ,0x00000007bff40000 ,0x00000007bff80000 ) to space 512 K, 0 % used [0x00000007bff80000 ,0x00000007bff80000 ,0x00000007c0000000 ) *老年代* ParOldGen total 260096 K, used 1471 K [0x00000006c0000000 , 0x00000006cfe00000 , 0x00000007bfe00000 ) object space 260096 K, 0 % used [0x00000006c0000000 ,0x00000006c016fc00 ,0x00000006cfe00000 ) *Metaspace空间,jdk8+* Metaspace used 3402 K, capacity 4500 K, committed 4864 K, reserved 1056768 K class space used 368K , capacity 388K , committed 512K , reserved 1048576K
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { new StackOverflowTest().heapSpace(); } private void heapSpace () { List<String> list = new ArrayList<>(); while (true ) { list.add(new String("abc" )); } } --- JVM ARGS: -server -Xmn2m -Xss1m -Xms1m -Xmx1m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB
java.lang.OutOfMemoryError:GC overhead limit exceeded
超出了GC开销限制
引起的内存溢出,这个错误不是特别常见,Sun 官方对此的定义:超过98%的时间用来做GC并且回收了不到2%的堆内存时会抛出此异常,可以使用参数-XX:-UseGCOverheadLimit 禁用这个检查,但是这个参数解决不了内存问题,只是把错误的信息延后,替换成 java.lang.OutOfMemoryError: Java heap space
Metaspace内存溢出
,Metaspace是jdk8+特有的东西,用来代替之前的PermGen,主要存储class名称、字段、方法、字节码、常量池、JIT优化代码等等,我们可以使用-XX:MetaspaceSize和-XX:MaxMetaspaceSize来指定其大小,一般情况下Metaspace不会发生OOM,Metaspace的使用量与JVM加载的class数量有很大关系:
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static ClassPool cp = ClassPool.getDefault();public static void main (String[] args) throws CannotCompileException { int i = 0 ; try { for (;; i++) { Class cz = cp.makeClass("com.example.demo.bean.DemoBean" + i).toClass(); } } catch (Exception e) { } finally { System.out.println(i); } } --- JVM ARGS: -XX:MetaspaceSize=10 m -XX:MaxMetaspaceSize=10 m -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 0.598 : [GC (Metadata GC Threshold) [PSYoungGen: 39345 K->10741 K(76288 K)] 39345 K->15811 K(251392 K), 0.0111319 secs] [Times: user=0.05 sys=0.01 , real=0.01 secs] 0.609 : [Full GC (Metadata GC Threshold) [PSYoungGen: 10741K->0K (76288 K) ] [ParOldGen: 5069K->15550K (139776 K) ] 15811K->15550K (216064 K) , [Metaspace: 9735K->9735K (1056768 K) ], 0.0504762 secs] [Times: user =0.29 sys=0.01 , real=0.05 secs] ... 0.754 : [GC (Last ditch collection) [PSYoungGen: 0 K->0 K(82944 K)] 15477 K->15477 K(472064 K), 0.0008113 secs] [Times: user=0.00 sys=0.00 , real=0.01 secs] 0.755 : [Full GC (Last ditch collection) [PSYoungGen: 0K->0K (82944 K) ] [ParOldGen: 15477K->15477K (607232 K) ] 15477K->15477K (690176 K) , [Metaspace: 9733K->9733K (1056768 K) ], 0.0204189 secs] [Times: user =0.08 sys=0.00 , real=0.02 secs] 5341 Exception in thread "main" java.lang.OutOfMemoryError: Metaspace at javassist.ClassPool.toClass(ClassPool.java:1170 ) at javassist.ClassPool.toClass(ClassPool.java:1113 ) at javassist.ClassPool.toClass(ClassPool.java:1071 ) at javassist.CtClass.toClass(CtClass.java:1275 ) at com.example.demo.jvm.MetaspceOOMTest.main(MetaspceOOMTest.java:13 ) Heap PSYoungGen total 82944 K, used 2390 K [0x000000076ab00000 , 0x0000000772c00000 , 0x00000007c0000000 ) eden space 82432 K, 2 % used [0x000000076ab00000 ,0x000000076ad55ab0 ,0x000000076fb80000 ) from space 512 K, 0 % used [0x0000000772b80000 ,0x0000000772b80000 ,0x0000000772c00000 ) to space 10752 K, 0 % used [0x0000000771700000 ,0x0000000771700000 ,0x0000000772180000 ) ParOldGen total 607232 K, used 15477 K [0x00000006c0000000 , 0x00000006e5100000 , 0x000000076ab00000 ) object space 607232 K, 2 % used [0x00000006c0000000 ,0x00000006c0f1d4c8 ,0x00000006e5100000 ) Metaspace used 9770 K, capacity 10084 K, committed 10240 K, reserved 1056768 K class space used 3165K , capacity 3214K , committed 3328K , reserved 1048576K
我们将Metaspace的初始大小和最大值都设置为10m,最终i
的值大概会在5340左右的时候报OOM,从FGC的日志可以看出,Metaspace在整个GC阶段都未进行任务的内存回收,直至被全部用完,具体的关于Metaspace的介绍可以看下PerfMa社区的这篇文章:https://club.perfma.com/article/210111
java.lang.OutOfMemoryError:Direct buffer memory
ByteBuffer. allocateDirect (int capability)是分配操作系统的本地内存,不在GC管辖范围之内,由于不需要内存拷贝所以速度相对较快,但如果不断分配本地内存,堆内存就会很少使用,那么JVM就不需要进行GC,那创建的DirectByteBuffer对象就不会被回收,就会出现堆内存充足但本地内存不足的情况,继续尝试分配本地内存就会出现OOM。
代码
1 2 3 4 5 6 7 public static void main (String[] args) { System.out.println("当前direct大小: " + (VM.maxDirectMemory() / 1024 / 1024 ) + " MB" ); ByteBuffer bb = ByteBuffer.allocateDirect(Math.toIntExact(VM.maxDirectMemory() + 10 )); } --- JVM ARGS: -XX:MaxDirectMemorySize=10 m
这里我们需要通过JVM参数-XX:MaxDirectMemorySize=10
将JVM本地最大使用内存设置为10MB,不然如果你本地剩余内存很大,那么就很难模拟出此错误
输出
1 2 3 4 5 6 当前direct大小: 10 MB Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:694 ) at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123 ) at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311 ) at com.example.demo.jvm.DirectBufferOOMTest.main(DirectBufferOOMTest.java:11 )
java.lang.OutOfMemoryError:unable create new native thread
线程创建的太多,导致无法继续创建线程,出现这个问题就要去使用jstack
导出线程栈查看具体情况
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { while (true ) { new Thread(new Runnable() { @Override public void run () { try { Thread.sleep(100000 ); } catch (InterruptedException e) { } } }).start(); } }
这一段代码必然会出现该ERROR,不论你的机器有多牛掰,你会发现出现了OOM之后,进程并未终止,这个时候你可以用jps
命令查看进程号,然后使用jstack pid
查看线程栈,会发现有非常多的线程处于TIMED_WAITING (sleeping)
状态:
1 2 3 4 5 "Thread-256" #267 prio=5 os_prio=31 tid=0x00007fccdd8cc000 nid=0x27d03 waiting on condition [0x0000700019b85000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.example.demo.jvm.NativeThreadOOMTest$1 .run(NativeThreadOOMTest.java:11 ) at java.lang.Thread.run(Thread.java:748 )