• JVM垃圾回收


    如何判断堆上的对象可以回收了?

     当我们发现,对象均没有任何来自栈的引用,那么这个对象就可以被回收了。

    objects on the heap are no longer needed when they don’t have a connection to the stack.

    1. Person p1 = new Person(); // 1
    2. Person p2 = new Person(); // 2
    3. Person p3 = new Person(); // 3
    4. List personList = Arrays.asList(p1, p2, p3); // 4
    5. p1 = null; // 5
    6. personList = null; // 6

    在4执行完成后,我们的内存空间大概是这样的:

     可以看到,三个Person对象被三个Person变量引用的同时,被一个List也在引用

    当执行5时,P1指向的引用会被释放吗?不会。

     因为这个对象除了被p1直接引用外,还被persons的List间接引用,因此尚不可回收。

    当执行至6时,这个对象可以被回收了吗?可以的。

     此时不再有栈上的变量指向它,因此可以回收。(LIst Object也可以被回收了)

    标记对象

    将继续引用的对象标记为1,不再引用的对象保持为0,这样就可以回收标记是0的对象了。

    例如,在上面的例子中:

    1. Person p1 = new Person(); // 1
    2. Person p2 = new Person(); // 2
    3. Person p3 = new Person(); // 3
    4. List personList = Arrays.asList(p1, p2, p3); // 4
    5. p1 = null; // 5

    初始还没有开始标记时,几个标志位置均为0

     开始标记后,

    标记算法

    1. stop the world

    在标记的过程中,很可能还会被重新引用,或者产生新的对象,很容易和标记线程发生冲突,因此,stop the world简单粗暴,将暂停应用内的线程,开始正式标记工作。

    2. 引用计数法

    在对象上面维护一个引用计数器,每有一个引用指向自己,计数器+1,每有一个引用不再指向自己,计数器-1,垃圾回收那些计数器为0的对象。

    看起来引用计数法不会stop the world,但是,可能会因为循环引用导致内存泄漏,无法回收。

    1. class Nest {
    2. private Nest;
    3. public Nest getNest() {
    4. return nest;
    5. }
    6. public void setNest(Nest nest) {
    7. this.nest = nest;
    8. }
    9. }
    1. public class IslandOfIsolation {
    2. public static void main(String[] args) {
    3. Nest n1 = new Nest(); // 1
    4. Nest n2 = new Nest(); // 2
    5. n1.setNest(n2); // 3
    6. n2.setNest(n1); // 4
    7. n1 = null; // 5
    8. n2 = null; // 6
    9. }
    10. }

    我们发现,执行完4步骤,在n1和n2之间互相引用了彼此,各自计数为2

     执行完步骤6,此时计数仍不为0,因为n1和n2之间互相引用了彼此

     但是,这两个对象实际上跟栈已经没有关联了,应该被回收。所以引用计数法无法解决循环引用的问题。

    清除对象

    常规清除法

    常规清除法就直接把没有标记的对象直接清掉,不做其他处理,这会带来的问题是,内存碎片化

     删除后,会空出内存位置,可以在空的位置继续创建对象,但是,来个占用内存较大的对象可能就没法创建了。

    清除整理法

    清除整理法是两个步骤:

    第一个步骤与常规清除的办法一致,直接删除未标记的对象。

    第二个步骤,将对象移动内存位置,将间隙填满,解决碎片化的问题。

    复制清除法

    将标记的对象复制到另外一个内存区域,然后清除当前的整个内存区域

     然后清除上面整个内存空间

    几种常见GC的实现

    类型详情适用场景
    Serial GC采用单线程、Stop the World的方式进行垃圾回收,对于新生代采用标记-复制法,对于老年代采用的是标记-整理法。适用于小型应用
    Parallel GC采用多线程、Stop the World的方式进行垃圾回收,对于新生代采用标记-复制法,对于老年代采用的是标记-整理法。jdk 1.8 默认模式
    CMS GC

    Concurrent Mark Sweep Garbage Collect

    采用Stop the World 的方式回收新生代,标记-复制法。

    两次Stop the World的方式回收老年代,标记-清除法。尽可能与应用程序并行执行,如果内存极度不足则暂停应用的线程。

    G1 GC

    将内存划分为多个更小的区域,在其内部追踪可达对象和可回收对象,在小区域之间进行复制,可以在复制的同时实现内存整理。依然会Stop the World,但是时间更短。

    Z GC

    在回收的过程中,会采用引用重新映射的方式解决碎片化的问题。stop the world时间很短。

    自java15开始,仅支持64位系统

    GC的监控指标

    内存分配速率:在内存中创建对象的速率。

    堆的体量:堆中存活的对象个数和大小。

    更新速率:对象更新的速率。

    平均存活时间:对象平均存活时间,有的在内存中很快释放,有的需要存储很久。

  • 相关阅读:
    微调Qwen2大语言模型加入领域知识
    项目管理证书 PMP 的含金量高吗?
    数据结构之线性表中的栈和队列【详解】
    全链路自动化测试
    Spring MVC中Restful风格引入
    Redis分布式锁
    USACO18OPEN Talent Show G
    【java核心技术】Java知识总结 -- 继承
    深度解析服务发布策略之金丝雀发布
    Windows11恢复组策略编辑器功能的方法
  • 原文地址:https://blog.csdn.net/Day_and_Night_2017/article/details/127781623