首先我提出几个问题:
-
Java 的内存/垃圾回收机制是什么?
-
什么是内存碎片 如何解决?
Java 不选用引用计数是因为它很难解决对象之间相互循环引用的问题。
为何要选择 这些地方作为 GC ROOT? 因为首先你需要确定的 GC ROOT 一定是对象存活的对象,而上图这些位置的对象都能保证存活,或者是不被 GC 所波及,因为 GC 所主要处理的位置是堆。
对于 Java 的内存回收机制我们主要分为 3 种:
-
标记-清除算法
- 不足:效率不高,容易产生内存碎片。
-
复制算法
-
效率高,复制成本大。
-
不足:如果是 1:1 模型,成本太高。
-
分配担保策略
-
-
标记-整理算法
- 因为老年代的对象生存率高,所以使用复制算法效率会很差,就提出了这个标记-整理算法。
我们可以发现每个算法都有他的优劣点,所以 JVM 开发者们设计出了 一种名叫 ”分代收集“的方式进行垃圾回收。
新生代选用复制算法,因为剩下的存活对象少
老年代选用"标记—清理","标记—整理"的算法做
前置知识 什么是内存碎片?
内部碎片
分配内存到进程 A,内存被进程占据了而不被利用,同时系统也无法利用这块内存,直到进程 A 被终结,释放内存。
外部碎片
还没被分配出去的内存太少了不足分配给下一个进程,又或者多个不连续的内存总空间长度能满足新申请的进程,但是由于地址是不连续的内存,无法分配给新进程。
Java 中的内存碎片可以理解为外部碎片,因为标记-清除算法只负责将标记的需要回收的内存给回收,对于一块内存,会留下很多小的空的位置,但是这些位置不足以去申请一个连续的对象。
对于内存碎片的解决,我们首先要明确,产生内存碎片的主要是老年代,所以我们主要聊一下这个 CMS 如何解决内存碎片。
CMS 提供了 -XX:+UseCMSCompactAtFullCollection 用于在 CMS 收集器顶不住要进行 FullGC 时开启内存碎片的合并整理过程。虽然内存碎片没有了,但是停顿时间会变长很多很多,虚拟机设计者提供了 -XX:CMSFullGCsBeforeCompaction,在上一次 CMS 并发 GC 执行过后,到底还要再执行多少次 full GC 才会做压缩。默认是 0,也就是在默认配置下每次 CMS GC 顶不住了而要转入 full GC 的时候都会做压缩。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于