前提介绍
什么是垃圾收集(GC
在JVM领域中GC(Garbage Collection)翻译为 “垃圾收集“,Garbage Collector翻译为 “垃圾收集器”。
分代模型(Generational Model
-
大部分对象很快就不再使用
- 还有一部分不会立即无用,但也不会持续(太长时间
这形成了分代数据模型。基于这一结构, VM中的内存被分为年轻代(Young Generation和老年代(Old Generation,老年代有时候也称为年老区(Tenured。如下所示。
分代模型出现问题
在不同分代中的对象可能会互相引用, 在收集某一个分代时就会成为 “事实上的” GC root。当然,要着重强调的是,分代假设并不适用于所有程序。
分代模型适合场景
新生代(Eden,伊甸园
卡片标记
JVM只需要记住Eden区中 “脏”对象的粗略位置,可能有老年代的对象引用指向这部分区间。
存活区(Survivor Spaces
此外GC会跟踪记录每个存活区对象存活的次数,每次分代GC完成后,存活对象的年龄就会+1。当年龄超过提升阈值(tenuring threshold,就会被提升到老年代区域。
MaxTenuringThreshold的判定
-XX:+MaxTenuringThreshold来指定上限。如果设置 -XX:+MaxTenuringThreshold=0
, 则GC时存活对象不在存活区之间复制,直接提升到老年代。现代 JVM 中这个阈值默认设置为15个GC周期。这也是HotSpot中的最大值。
老年代(Old Generation
老年代GC发生的频率比年轻代小很多。同时, 因为预期老年代中的对象大部分是存活的, 所以不再使用标记和复制(Mark and Copy算法。而是采用移动对象的方式来实现最小化内存碎片。老年代空间的清理算法通常是建立在不同的基础上的。原则上,会执行以下这些步骤:
- 通过标志位(marked bit,标记所有通过 GC roots 可达的对象.
- 删除所有不可达对象
- 整理老年代空间中的内容,方法是将所有的存活对象复制,从老年代空间开始的地方,依次存放。
永久代(PermGen
Java8之前有一个特殊的空间,称为“永久代”(Permanent Generation。
元数据区(Metaspace
常见的垃圾回收思想的误区
在我们的日常生活中垃圾收集主要就是找到垃圾并进行清理,这与我们JVM的运作机制恰恰相反,JVM中的垃圾收集器跟踪和标记所有正在使用的对象,并把其余部分的对象当做垃圾对象。
标记可用对象,而不是垃圾对象。常常会有人吧这两者理解错误和混乱。
常见的垃圾回收类型
垃圾回收类型主要是通过回收的范围进行界定和划分。具体的JVM回收区域如下图所示。
Java8之前
Java8之后
- Minor GC:年轻代垃圾回收机制,属于轻量级GC,主要面向于年轻代区域的垃圾对象进行回收。
- Major GC:老年代垃圾回收机制,属于重量级GC,主要面向于老年代区域的垃圾对象进行回收。
- Full GC:完全化GC,属于全量极GC,大致角度而言Major GC和Full GC差不多,其实具体分析,FullGC的范围是面向于整体的Heap堆内存。
GC的优点和缺点(GC Benefits/Cost)
好处
- 提高系统的可靠性和稳定性
- 内存管理与程序设计的解耦
- 调试内存错误所花费的时间更少
- 悬挂程序点/内存泄漏不会发生
注意:Java程序没有内存泄漏;“不意味着对象存储地址”更准确)
坏处
- GC暂停的时间长度
- CPU/内存利用率
Minor GC
年轻代内存的垃圾收集称为Minor GC。那什么时候会触发MinorG以及出发MinorGC得我条件是什么?
触发MinorGC的时机
MinorGC回收的瓶颈
Eden区的对象基本上都是垃圾,也不怎么复制到Survior区/老年代。如果情况不是这样, 大部分新创建的对象不能被垃圾回收清理掉,则 Minor GC的停顿就会持续更长的时间。
MinorGC回收的范围
-
主要面向的是Survior区之间的相互引用,此种场景的生命周期较短,属于年轻代之内的对象之间的引用关系。
主要是面向于老年代到年轻代的所引用的对象范围,例如,它会将从老年代指向年轻代的引用都被认为是GC Root,(而从年轻代指向老年代的引用在标记阶段全部被忽略)。
Major GC vs Full GC
Major GC清理的是老年代空间(Old space,MajorGC是由Minor GC触发的,所以很多情况下这两者是不可分离的,G1这样的垃圾收集算法执行的是部分区域垃圾回收。
Minor GC、MajorGC和FullGC执行效果
大部分情况下,发生在年轻代的Minor GC次数会很多,会引起STW,也就是全局化暂停执行业务线程的行为,但是时间很短(几乎可以忽略不计)。而Major GC和Full GC也会造成全局化暂停的效果。所以一般情况下尽可能减少MajorGC和FullGC是什么必要的,但是也不能“一棒子打死一船人”。必要的时候还是需要触发少量几次Major GC以及FullGC,进而释放一些RSS常驻内存。
垃圾收集(GC的原理
自动内存管理(Automated Memory Management
引用计数(Reference Counting
共享指针方式的引用计数法,可以应用到所有对象。许多语言都采用这种方法,包括 Perl、Python 和 PHP 等。下图很好地展示了这种方式:
这里指的不是全部)集中在于当前正在执行的方法中的局部变量或者是静态变量等。在这里主要我指的是Java。
-
蓝色的圆圈表示可以引用到的对象,里面的数字就是被引用计数器。
- 灰色的圆圈是各个作用域都不再引用的对象,可以被认为是垃圾,随时会被垃圾收集器清理。
循环引用(detached cycle)的问题
- 红色线路和红色圆圈对象实际上属于垃圾引用以及垃圾对象,但由于引用计数的局限,所以存在内存泄漏,永远都无法进行回收该区域的对象内存。
循环引用(detached cycle)的解决方案
精华推荐 | 【JVM深层系列】「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC算法分析)
编程笔记 » 精华推荐 | [JVM深层系列]「GC底层调优系列」一文带你彻底加强夯实底层原理之GC垃圾回收技术的分析指南(GC原理透析)