模拟OOM代码
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(() -> { for (int i = 0; i < Integer.MAX_VALUE; i++) { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //获取当前堆的大小 long heapSize = Runtime.getRuntime().totalMemory(); System.out.println("current heap size:" + heapSize/1024/1024); //获取堆的最大大小 //超过将抛出 OutOfMemoryException long heapMaxSize = Runtime.getRuntime().maxMemory(); System.out.println("max heap size:" + heapMaxSize/1024/1024); //获取当前空闲的内存 long heapFreeSize = Runtime.getRuntime().freeMemory(); System.out.println("free heap size:" + heapFreeSize/1024/1024); } }); for (int j = 0; j < 5; j++) { executorService.execute(() -> { List<String> list = new ArrayList<>(); for (int i = 0; i < Integer.MAX_VALUE; i++) { // try { // Thread.sleep(30); // } catch (InterruptedException e) { // e.printStackTrace(); // } list.add(UUID.randomUUID().toString()); System.out.println("thread name:" + Thread.currentThread().getName() + ",list size:" + list.size()); } }); } executorService.shutdown(); } |
设置参数并启动
启动的时候,在idea的VM options里填上-Xmx10m
,设置一下最大堆内存

可以看到不久出现了错误
1 |
Exception in thread "pool-1-thread-2" java.lang.OutOfMemoryError: GC overhead limit exceeded |
字面意思就是垃圾回收的内存超出限制,达到天花板了。
OutOfMemoryError
是java.lang.VirtualMachineError
的子类,当JVM
资源利用出现问题时抛出,更具体地说,这个错误是由于JVM
花费太长时间执行GC
且只能回收很少的堆内存时抛出的。根据Oracle
官方文档,默认情况下,如果Java
进程花费98%
以上的时间执行GC
,并且每次只有不到2%
的堆被恢复,则JVM
抛出此错误。换句话说,这意味着我们的应用程序几乎耗尽了所有可用内存,垃圾收集器花了太长时间试图清理它,并多次失败。
为什么会这样?
这首先要了解哪些占用内存的对象会被垃圾回收器回收,那就是没有被引用的对象,随着我们的list越来越大,list会被丢到old区,但是每次Full GC的时候又不能回收list。
JVM使用的是什么垃圾回收器?
可以使用下面两个命令来查看
1 |
[root@localhost ~]# java -XX:+PrintFlagsFinal -version |

1 |
[root@localhost ~]# java -XX:+PrintCommandLineFlags -version |

可以看到使用的是+UseParallelGC,jdk是1.8.0_152
如何定位解决有问题的代码?
打开java自带的jmc.exe工具,可以在热点类里面找

或者jconsole.exe

理想的解决方案是通过检查可能存在内存泄漏的代码来发现应用程序所存在的问题,这时需要考虑:
- 应用程序中哪些对象占据了堆的大部分空间?(
What are the objects in the application that occupy large portions of the heap?
) - 这些对象在源码中的哪些部分被使用?(
In which parts of the source code are these objects being allocated?
)
通过更改JVM
启动配置来增加堆大小,或者在JVM
启动配置里增加-XX:-UseGCOverheadLimit
选项来关闭GC Overhead limit exceeded
。
1 |
java -Xmx1024m -XX:-UseGCOverheadLimit com.xxx.xxxClassName |
但增加-XX:-UseGCOverheadLimit
选项的方式治标不治本,JVM
最终会抛出java.lang.OutOfMemoryError: Java heap space
错误。
最终还是要找到有问题的代码进行修改。