一.Java 内存区域划分
(图片来源,Java虚拟机规范)
除上图运行时数据区域之外,还有一块内存区域被频繁的使用,那就是直接内存,未定义在 《Java虚拟机规范中》中。
二.垃圾回收
1.可达性分析:
当一个对象到 GC Roots 没有任何引用链时,即从 GC Roots 到这个对象不可达,则说明该对象不可用,可被垃圾回收器回收。
注:如果类实现了 finalize()
方法,则被回收前会先调用该方法,且只会调用一次。
2.垃圾收集算法:
标记清除 (Mark-Sweep)
标记出所有需要回收的对象,标记完成后统一回收被标记的对象。
主要缺点:效率不高;产生大量内存碎片。内存碎片太多可能会导致分配较大对象时,找不到连续内存而触发 GC。
复制 (Copying)
将内存划分为大小相等的两块,每次只使用其中一块。当这块内存用完了,则将存活对象复制到另一块上,然后将已使用的内存空间一次清理掉。适用于多用于新生代。
优点:算法简单高效,且无内存碎片
缺点:牺牲了空间
标记整理 (Mark-Compact)
标记出需要回收的对象,让所有存活的对象都向一端移动,然后清理掉端另一边的内存。
分代收集算法
根据对象存活周期的不同,将内存分为几块。一般为新生代和老年代(jdk 1.8移除了永久代)。新生代存活对象少,采用复制算法;老年代对象存活率高,采用标记-清除或标记-整理算法。
IBM 研究表明,新生代中 98% 对象存活率很低,所以不需要按照 1:1 的比例来划分内存空间,而是使用一块较大的 Eden
区和两块较小的 Survivor
区,每次 YGC 时将 Eden
区和其中一块 Survivor
区的存活对象复制到另一个 Survivor
区,再一次性清除 Eden
区和使用过的 Survivor
区。
Eden
区和 Survivor
区默认比例大小为 8:1,牺牲 Young 区10%的空间,可通过 SurvivorRatio
参数指定。
3.垃圾收集器
HotSpot 虚拟机的垃圾收集器(图片网上找的):
如果两个收集器之间存在连线,则说明它们可以搭配使用。
3.1 CMS 收集器
基于标记-清除算法。只有 CMS initial mark
和 CMS Final Remark
阶段会 Stop The World
。
初始标记(CMS initial mark)
标记那些直接被 GC Roots 引用或者被年轻代存活对象所引用的所有对象,会 Stop The World
。
并发标记(CMS concurrent mark)
遍历整个 old 区,并发标记存活对象。和应用线程并行执行,此时有些对象可能会改变可达状态。
并发预清理(CMS concurrent preclean)
并发阶段,和应用线程并行执行。在前面并发执行的阶段中,有些对象的引用可能会发生变化,JVM 会将包含该对象的区域 (Card
) 标记为 Dirty
(即 Card Marking
)。
在 pre-clean
阶段,那些能从 Dirty Card
对象到达的对象也会被标记,标记完成之后,Dirty Card
标记会被清除。
此外,还会执行一些必要的清理和为 Final Remark
阶段做一些准备工作。
并发预清理(CMS concurrent abortable preclean)
并发执行,这个阶段会尽可能的减轻 Final Remark
阶段 Stop The World
的压力。
这个阶段的时间依赖于很多因素,会重复做相同的事情,直至满足一些条件,如:重复的次数、有效的工作量、始终时间等。
重新标记(CMS Final Remark)
完成标记整个 old 区所有的可达对象,会 Stop The World
。