概述
接着上篇性能调优(三),通过一些实例继续分析性能问题的现象和原因,希望能给研发兄弟们一点点参考价值。
工具及版本
工具 | 版本 | |
---|---|---|
Arthas | 3.5.2 | |
perf | 3.10.0-1160.31.1.el7.x86_64.debug | |
JDK | 1.8.0_221 |
实例
根据 JVM8 规范,JVM 运行时内存共分为虚拟机栈、堆、元空间、程序计数器、本地方法栈五个部分。还有一部分内存叫直接内存,属于操作系统的本地内存,也是可以直接操作的。接下来通过代码来演示下内存溢出的一些异常。
11.png堆溢出
-
编写以下代码验证
/** * -Xmx32M -Xms32M * */ @GetMapping("/heapOom") public String heapOomErr() { List<byte[]> list = new ArrayList<>(); while(true){ list.add(new byte[8*1024*1024]); } }
-
启动虚拟机的时候配置-Xmx32M -Xms32M,触发溢出的代码,一会后报错如下:
java.lang.OutOfMemoryError: Java heap space at com.lqd.jvm.monitor.MemoryController.heapOomErr(MemoryController.java:24) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251] at
-
解决方案:在保证没有内存泄漏的情况下,一般可以适当调整-Xmx -Xms。
元空间溢出
-
编写以下代码
/** * -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M * */ @GetMapping("/nonheap") public String nonheap() { while(true) { classList.addAll(Metaspace.createClasses()); } }
-
启动虚拟机的时候配置 -XX:MetaspaceSize=32M -XX:MaxMetaspaceSize=32M,触发溢出的代码,一会后报错如下:
java.lang.OutOfMemoryError: Metaspace at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_251] at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[na:1.8.0_251] at java.lang.ClassLoader.defineClass(ClassLoader.java:635) ~[na:1.8.0_251] at com.lqd.jvm.monitor.Metaspace.createClasses(Metaspace.java:39) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.nonheap(MemoryController.java:48) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251] at
-
解决方案:在保证没有内存泄漏的情况下,一般可以适当调整-XX:MetaspaceSize -XX:MaxMetaspaceSize。
垃圾回收超时
-
代码不变,这时设置-Xmx32M -Xms32M,触发/nonheap消耗元空间的内存空间,一会后报错如下,这是因为经过多次长时间的GC操作都无法回收,导致可用内存越来越少。JVM花费了98%的时间进行垃圾回收,而只得到2%可用的内存,频繁的进行内存回收(最起码已经进行了5次连续的垃圾回收),JVM就会曝出java.lang.OutOfMemoryError: GC overhead limit exceeded错误。
java.lang.OutOfMemoryError: GC overhead limit exceeded at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.0_251] at java.lang.ClassLoader.defineClass(ClassLoader.java:756) ~[na:1.8.0_251] at java.lang.ClassLoader.defineClass(ClassLoader.java:635) ~[na:1.8.0_251] at com.lqd.jvm.monitor.Metaspace.createClasses(Metaspace.java:39) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.nonheap(MemoryController.java:48) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251]
-
解决方案:检测代码干掉内存泄漏的代码。
栈溢出
-
编写以下代码
/** * -Xss * */ @GetMapping("/stackOom") public void stackOom() { int num =1 ; testStack(); } private void testStack(){ int i = 2,temp = 0; i = temp; temp = 2; this.testStack(); }
-
启动虚拟机的时候配置-Xss128k,触发溢出的代码,一会后报错如下:
java.lang.StackOverflowError: null at com.lqd.jvm.monitor.MemoryController.testStack(MemoryController.java:57) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.testStack(MemoryController.java:57) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.testStack(MemoryController.java:57) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.testStack(MemoryController.java:57) ~[classes/:na] at com.lqd.jvm.monitor.MemoryController.testStack(MemoryController.java:57) ~[classes/:na]
-
解决方案:在保证没有内存泄漏的情况下,一般可以适当调整-Xss。
直接内存溢出
-
编写以下代码
/** * -XX:MaxDirectMemorySize * */ @GetMapping("/directOom") public void directOom() { try { int i = 0 ; Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); Unsafe unsafe = (Unsafe) field.get(null); while(true){ i++ ; unsafe.allocateMemory(1024 * 1024) ; } } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } }
-
启动虚拟机,触发溢出的代码,一会后报错如下:
java.lang.OutOfMemoryError: null at sun.misc.Unsafe.allocateMemory(Native Method) ~[na:1.8.0_251] at com.lqd.jvm.monitor.MemoryController.directOom(MemoryController.java:66) ~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_251] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_251] at # # There is insufficient memory for the Java Runtime Environment to continue. # Native memory allocation (malloc) failed to allocate 32744 bytes for ChunkPool::allocate # An error report file with more information is saved as:
-
解决方案:在保证没有内存泄漏的情况下,一般可以适当调整-XX:MaxDirectMemorySize。
网友评论