目录
在先前的博客中已经分析了JAVA对象内存分配,对象布局和对象访问定位,如果没有看过该博客的客官请移步Java中的对象_熟透的蜗牛的博客-CSDN博客。那么JAVA在运行过程中可能时时刻刻都在创建新的对象,那么在创建对象时如果没有分配到内存的时候会发生什么?首先可能会频繁的进行垃圾回收,如果回收的速度赶不上创建的速度,那么这时候当内存达到一定量时就会发生内存溢出。
内存溢出(out of memory):是指程序在申请内存时,没有足够的内存空间供其使用,出现OOM。比如我们的JVM内存还剩10MB,但是创建了一个20MB的对象,那么此时JVM已经没有足够的空间可以盛放这个对象,此时就会发生内存溢出的问题。
内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间。比如一个对象占用了10M的空间,但是它使用完了,一直不释放。如果一次内存泄漏可以容忍,但是有很多的内存泄漏,不管有多大的内存迟早会被用光,而导致的后果就是内存溢出。
在《JAVA虚拟机规范》中指出,程序计数器是唯一一个不会产生OOM的区域。那么言外之意就是其他几个区域都有可能产生内存溢出。下面一个一个开始分析。
为了更好的演示内存溢出,修改JVM的参数,我的jdk版本为17
-Xmx20M -Xms20M -Xmn10M -XX:+HeapDumpOnOutOfMemoryError
-Xmx20M 堆最大内存
-Xms20M 堆初始内存
-Xmn10M 堆内新生代的大小
-XX:+HeapDumpOnOutOfMemoryError 当发生内存溢出时dump出当前内存堆的快照
代码演示
- public class TestHeapOOM {
-
- static class User {
- private long id;
- private String name;
-
- User() {
- this.id = 100L;
- this.name = "熟透的蜗牛";
- }
- }
-
- public static void main(String[] args) {
- ArrayList
userList = new ArrayList<>(); - while (true) {
- User user = new User();
- userList.add(user);
- }
- }
- }
运行结果如下,发生了OOM。

这两个区域在《JAVA虚拟机规范》有两种异常
为了更好的演示修改JVM参数
-Xmx20M -Xms20M -Xmn10M -Xss180k -XX:+HeapDumpOnOutOfMemoryError
-Xss180k设置每个线程可使用的内存大小,即栈的大小。在jdk17最小值为180k,其他jdk版本和不同的操作系统这个值会存在差异。在相同物理内存下,减小这个值能生成更多的线程,当然操作系统对一个进程内的线程数还是有限制的,不能无限生成。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,可能会出现内存溢出的错误。
代码演示
- public class TestStackOOM {
-
- private int length = 1;
-
- //递归
- public void stackLeak() {
- length++;
- stackLeak();
- }
-
- public static void main(String[] args) {
- TestStackOOM testStackOOM = new TestStackOOM();
- try {
- testStackOOM.stackLeak();
- } catch (Throwable e) {
- System.out.println("栈深度为>>>>>>>>>>>>>>:" + testStackOOM.length);
- throw e;
- }
- }
- }
运行结果

对于多线程情况下产生的内存溢出,感兴趣的可以自己试试。
由于JDK8之后HotSpot放弃了永久代,改为元空间实现方法区,而元空间则不再占用堆内存的大小,而是使用本地内存实现,所以除非在极端情况下,电脑没有一点内存分配给方法区,否则理论上不会出现OOM异常了。
那么如何尽量避免内存溢出和内存泄漏呢?本人总结了几点。
内存溢出
内存泄漏