JAVA 中 分堆内存 和 堆外内存

堆外内存不是由JVM控制的,这个得开发人员自己手工调用方法去释放

堆内内存是由JVM控制,可以由JVM 通过算法自动 GC

JVM 垃圾回收有很多种收集器,我们这里讲的是CMS收集器,G1 收集器我们这里不做讲解

并且JAVA8 及以前版本默认是采用 CMS 收集器的,JAVA9及以后默认是G1

JAVA 在 JVM 启动的时候,就事先对堆内存进行了划分了几个模块

默认情况下 JVM 堆内存分配:

新生代 (占堆内存 1/3) 老年代 (占堆内存 2/3)
Eden Survivor0 Survivor1
80% 10% 10%

JVM 将堆内存划分了2个大模块

新生代: 当我们使用了一个新对象,申请内存的时候,就是先从新生代内存里申请

老年代: 当新生代的内存经过了15次 GC 还没被 GC 掉 或者 当新申请的内存比较大的时候,直接从老年代里分配

新生代 GC

新生代里又分划分了 Eden , Survivor0, Survivor1 三个区域

翻译过来的 就是 伊甸园 , 存活0区 , 存活1区

伊甸园是《圣经》故事中人类的始祖亚当和夏娃居住的乐园。所以作者也很意思。

实际 Eden , Survivor0, Survivor1 三个区域职责如下:

Eden : 当申请内存的时候,从 Eden 里申请(当申请的内存大于JVM启动参数 PretenureSizeThreshold 的值的时候,直接从 老年代里分配内存,默认值为0,所有都从Eden分配), 当 Eden 里不够内存的时候,会进行一次普通GC

Survivor : 当 Eden 内存不够的时候,普通GC, 将 Eden 里没被引用等的内存GC掉,把活下来的,转到 Survivor 区域

这里为什么不写是 Survivor0 或者 Survivor1

因为 Survivor0 和 Survivor1 两个区域干的活是一样, Survivor0 和 Survivor1 相交替干活的

可以理解为,每次GC,只有 其中一个可用。

例如,当前 Survivor0 和 Survivor1 都会空

第一次新生代GC

Eden 里GC存活来的内存,转移到 Survivor0, 并且给那些活下来的对象 “寿命”+1

第二次新生代GC

将Eden 和 Survivor0两个区域活下来的内存转移到 Survivor1

然后再将 Eden 和 Survivor0 两个都区域的全清空

并且给那些活下来的对象 “寿命”+1

第三次新生代GC

将Eden 和 Survivor1 两个区域活下来的内存转移到 Survivor0

然后再将 Eden 和 Survivor1 两个都区域的全清空

并且给那些活下来的对象 “寿命”+1

第 N 次新生代GC

将 “寿命” >= 15 的对象,转移到 老年代。

假如 Eden 和 Survivor 区域活下来的对象需要的空间 大于 Survivor0 的空间。则找出哪一个 寿命及以上的 总合大于 Survivor0 的空间,则将那个年龄及对上的 对象转移到 老年代(其实就是按年龄倒序排序,直接那个 Survivor 中其中一个 区域够存为止)

每次新生代GC 都是只用 Survivor 其中一个区域,把另一个空出来,待下一次的时候,这就是大家常说的 复制GC

但这样可以理解为至少有一个 Survivor 区域内存是没有真正存数据的

老年代 GC

新生代 GC 后 Survivor0或者Survivor1 不够存 新生代 GC 之后活下来的对象 的时候

会新生代 将 “寿命” >= 15 的对象 或者 Survivor0 不够存按年龄大小倒序排出来的对象 转移到 老年代

但是老年代的空间,也是有限的,默认情况下占当前 JVM 堆空间的 23

老年代普通GC

当老年代里的 连续内存空间 大于 新生代转移过来的内存块的时候,则会进行一次 老年代普通 GC

Full GC

当老年代里的 连续内存空间 小于 新生代转移过来的内存块的时候,则会进行 Full GC

Full GC 是对整个堆空间进行GC

如果Full GC后还是无法给新创建的对象分配内存,或者无法移动那些需要进入老年代中的对象,那么JVM抛出 Out Of Memory Error

小结

  1. JVM 是中整个堆当内存不够的时候,会进行 Full GC , 当Full GC 内存还是不够的时候,会抛出 Out Of Memory Error

  2. JVM 新生代,老年代内存分配比例是可以通过启动参数控制的

  3. Map ,Set , ArrayList 等对象中存存储的数据,需要开发人员手动 remove 掉,要不然GC 可能不会释放(除非WeakHaspMap等对象),很容易让堆占满,导致Full GC