空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能导致,程序分配较大对象时无法找到足够的连续内存,不得不提前出发另一次垃圾收集动作 。

文章插图
复制算法(Copying)- 新生代将可用内存按容量划分为大小相等的两块,每次只使用其中一块 。当这一块的内存用完了,就将存活着的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉 。
优点复制算法使得每次都是针对其中的一块进行内存回收,内存分配时也不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效 。
缺点将内存缩小为原来的一半 。在对象存活率较高时,需要执行较多的复制操作,效率会变低 。

文章插图
应用商业的虚拟机都采用复制算法来回收新生代 。因为新生代中的对象容易死亡,所以并不需要按照1:1的比例划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间 。每次使用 Eden 和其中的一块 Survivor 。
当回收时,将 Eden 和 Survivor 中还存活的对象一次性拷贝到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间 。Hotspot 虚拟机默认 Eden 和 Survivor 的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80% + 10%),只有10%的内存是会被“浪费”的 。
标记-整理算法(Mark-Compact)-老年代标记过程仍然与“标记-清除”算法一样,但不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉边界以外的内存 。

文章插图
分代收集算法根据对象的存活周期,将内存划分为几块 。一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点,采用最适当的收集算法 。
- 新生代:每次垃圾收集时会有大批对象死去,只有少量存活,所以选择复制算法,只需要少量存活对象的复制成本就可以完成收集 。
- 老年代:对象存活率高、没有额外空间对它进行分配担保,必须使用“标记-清理”或“标记-整理”算法进行回收 。
Java 内存为什么要将堆内存分区?对于一个大型的系统,当创建的对象及方法变量比较多时,即堆内存中的对象比较多,如果逐一分析对象是否该回收,效率很低 。分区是为了进行模块化管理,管理不同的对象及变量,以提高 JVM 的执行效率 。
堆内存分为哪几块?
- Young Generation Space 新生区(也称新生代)
- Tenure Generation Space养老区(也称旧生代)
- Permanent Space 永久存储区
- 对象优先分配在 Eden
- 大对象直接进入老年代
- 长期存活的对象将进入老年代
- 动态对象年龄判定
- 空间分配担保
- 当对象在堆创建时,将进入年轻代的Eden Space 。
- 垃圾回收器进行垃圾回收时,扫描Eden Space和A Suvivor Space,如果对象仍然存活,则复制到B Suvivor Space,如果B Suvivor Space已经满,则复制 Old Gen
经验总结扩展阅读
- JVM学习笔记——类加载和字节码技术篇
- 详解pyautogui模块
- 2022-11-4 VideoPipe可视化视频结构化框架新增功能详解
- 详细了解JVM运行时内存
- 【JVM】关于JVM,你需要掌握这些 | 一文彻底吃透JVM系列
- Istio Ambient Mesh七层服务治理图文详解
- JVM学习笔记——垃圾回收篇
- 详解AQS中的condition源码原理
- JVM学习笔记——内存结构篇
- FHQ Treap 详解