1.运行时数据区
2.JVM垃圾收集器
3.JVM回收机制
3.1 GC Root
GC Root 对象是一组特殊的对象,它们被认为是活动对象并且不会被垃圾回收。GC Root 对象包括以下几种类型:
-
虚拟机栈中引用的对象:即当前线程中的局部变量表中引用的对象。
-
方法区中类静态属性引用的对象:即类的静态变量所引用的对象。
-
方法区中常量引用的对象:即被常量池引用的对象。
-
本地方法栈中 JNI(Java Native Interface)引用的对象:即由 JNI 方法引用的对象。
-
虚拟机内部的引用:例如基本数据类型的类对象,由虚拟机内部引用的对象。
3.2 垃圾回收算法
以下是几种常见的垃圾回收算法:
-
引用计数算法(Reference Counting):
- 该算法通过在对象上维护一个引用计数器来实现。每当有一个指针引用该对象时,引用计数加一;当引用失效时,计数减一。当引用计数为零时,该对象被视为垃圾并被回收。
- 优点是简单且实时性好,但无法处理循环引用的情况。
-
标记-清除算法(Mark and Sweep):
- 该算法通过标记所有从根节点(如全局变量、调用栈等)可达的对象,然后清除未被标记的对象来实现。清除操作通常是将其内存标记为可用状态。
- 优点是能够处理循环引用,并且能够释放整个不再使用的对象,但可能会造成内存碎片化问题。
-
标记-整理算法(Mark and Compact):
- 该算法结合了标记-清除算法和内存整理操作。在标记阶段,它会标记所有可达的对象;然后,在整理阶段,它会将所有存活的对象向一端移动,以便连续地分配未使用的内存块。
- 优点是能够解决内存碎片化问题,但需要更多的内存移动操作。
-
分代回收算法(Generational Garbage Collection):
- 该算法根据对象的存活时间将内存划分为不同的代(generations)。通常,新创建的对象会放入年轻代,而经过多次垃圾回收仍然存活的对象会被提升到老年代。
- 这种算法基于“新生代假设”:大多数对象很快就会变得不可达。因此,对年轻代的频繁垃圾回收可以提高效率。
-
复制算法(Copying Garbage Collection):
- 该算法将内存空间划分为两个区域,通常称为“From Space”和“To Space”。在垃圾回收时,它会将存活的对象从一个区域复制到另一个区域,并且清除未复制的对象。然后,这两个区域的角色被互换,以确保内存的连续性。
- 优点是简单且高效,但是需要额外的内存空间来存储复制后的对象
3.3 分代回收策略
堆内存分为新生代和老年代,新生代的回收称为Minor GC,老年代(Old Generation)的垃圾回收,通常被称为“Major GC”或“Full GC”
新生代回收过程:
- 绝大多数刚刚被创建的对象会存放在 Eden 区。
- 当
Eden
区第一次满的时候,会进行垃圾回收。首先将 Eden
区的垃圾对象回收清除,并将存活的对象复制到 S0
,此时 S1
是空的。
- 下一次
Eden
区满时,再执行一次垃圾回收。此次会将 Eden
和 S0
区中所有垃圾对象清除,并将存活对象复制到 S1
,此时 S0
变为空。
- 如此反复在
S0
和 S1
之间切换几次(默认 15 次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将它们转移到老年代中。
老年代回收过程
- 老年代中的对象相对稳定,因此 Major GC(通常称为 FULL GC)的执行频率较低。通常情况下,在进行 FULL GC 之前,系统会先执行一次 Minor GC,将一些新生代的对象晋升至老年代。这种策略使得系统可以尽可能地利用老年代的空间,直到空间不足时才会触发 FULL GC。
- FULL GC 的触发条件还包括当无法找到足够大的连续空间来分配给新创建的较大对象时。这时,系统可能会提前触发一次 Major GC 来进行垃圾回收,以释放出足够的空间。
- 在执行 FULL GC 时,通常会采用标记-清除算法:首先扫描老年代中的所有对象,标记出存活的对象,然后回收未标记的对象。由于 FULL GC 的扫描和回收过程相对耗时,因此 FULL GC 的执行时间通常较长。
- 此外,FULL GC 可能会导致内存碎片化问题,为了减少内存损耗,通常需要进行内存碎片的合并或者标记处理,以便下次直接分配。
- 当老年代无法容纳新的对象或者无法找到足够大的连续空间时,系统会抛出OOM异常。
分享到: