-Xms8g -Xmx8g -Xmn3g -Xmx:设置堆的最大内存 -Xms:设置堆的初始内存大小 -Xmn:设置年轻代大小新版本改为:
-Xms8g -Xmx8g -Xmn8g看到这里,屏幕前的一众同事都无语啊……
2. Young 区空间还是不够,对象还是得放到 Old 区,Old 区空间不够,卒,喜提OOM
4. Eden 区 + S0 区 GC 后,S1 区放不下
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Random; @SpringBootApplication public class OomApplication { static final byte[] ARRAY = new byte[128 * 1024 * 1024]; public static void main(String[] args) { SpringApplication.run(OomApplication.class, args); } @RestController public static class OomExampleController { @GetMapping("/oom") public int oom() { byte[] temp = new byte[128 * 1024 * 1024]; temp[0] = (byte) 0xff; temp[temp.length - 1] = (byte) 0xef; int noise = new Random().nextInt(); ARRAY[0] = (byte) (temp[0] + temp[temp.length - 1] + noise); return ARRAY[0]; } } }使用mvn clean package命令打包后,我们用下面的命令启动它:
java -Xms512m -Xmx512m -Xmn512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar oom-1.0.0-RELEASE.jar然后借助Apache的ab.exe,完成我们的验证测试。先是以1个并发访问100次上面的SpringBoot接口:
ab -c 1 -n 100 http://localhost:8080/oom你会发现,它居然是可以正常运行的,然后我们模拟用户负载上来之后的情况,使用2个并发访问100次:
ab -c 2 -n 100 http://localhost:8080/oom如果前面的步骤都没错,此时应该在 SpringBoot 应用控制台看到大量的 OOM 错误,如下图所示:
// 堆代码 duidaima.com {Heap before GC invocations=279 (full 139): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 0% used [0x00000000f8100000,0x00000000f8100000,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K 2023-04-07T01:44:25.348+0800: 57.446: [GC (Allocation Failure) --[PSYoungGen: 273877K->273877K(458752K)] 274384K->274384K(459264K), 0.0441401 secs] [Times: user=0.06 sys=0.30, real=0.04 secs] Heap after GC invocations=279 (full 139): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 9% used [0x00000000f8100000,0x00000000f86e2070,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K } {Heap before GC invocations=280 (full 140): PSYoungGen total 458752K, used 273877K [0x00000000e0080000, 0x0000000100000000, 0x0000000100000000) eden space 393728K, 69% used [0x00000000e0080000,0x00000000f0bf5798,0x00000000f8100000) from space 65024K, 0% used [0x00000000fc080000,0x00000000fc080000,0x0000000100000000) to space 65024K, 9% used [0x00000000f8100000,0x00000000f86e2070,0x00000000fc080000) ParOldGen total 512K, used 506K [0x00000000e0000000, 0x00000000e0080000, 0x00000000e0080000) object space 512K, 98% used [0x00000000e0000000,0x00000000e007e910,0x00000000e0080000) Metaspace used 35959K, capacity 38240K, committed 38872K, reserved 1083392K class space used 4533K, capacity 4953K, committed 5080K, reserved 1048576K 2023-04-07T01:44:25.392+0800: 57.490: [Full GC (Ergonomics) [PSYoungGen: 273877K->142631K(458752K)] [ParOldGen: 506K->506K(512K)] 274384K->143137K(459264K), [Metaspace: 35959K->35959K(1083392K)], 0.0248171 secs] [Times: user=0.14 sys=0.00, real=0.03 secs]接着无需改动任何代码,我们调整下启动参数,像这样:
java -Xms512m -Xmx512m -Xmn64m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log -jar oom-1.0.0-RELEASE.jar你会发现它又可以了。这是一个为了验证而打造的极端例子,实际上生产的应用情况会比这个复杂得多,但这并不妨碍我们理解它的意图。
这是一场典型的”人祸“,来源于某个同事的”调优“,比起追究责任,更重要的是带给我们的启发:
1.即使是应用启停脚本,也应该作为程序的一部分,纳入测试验证流程和上线检查清单,禁止随意变更;
2.很多时候,默认的就是最好的,矫枉则常常过正。