首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java垃圾收集器

Java垃圾收集器

作者头像
秃头哥编程
发布于 2019-06-04 09:43:28
发布于 2019-06-04 09:43:28
56600
代码可运行
举报
文章被收录于专栏:秃头哥编程秃头哥编程
运行总次数:0
代码可运行

GC(垃圾收集器)算是Java语言的一大特色,不同于C/C++要我们手动释放内存,GC能够帮我们回收90%以上的“垃圾”。下面就来介绍一下垃圾收集器。

1. Java中如何定义一个“垃圾” 2. 什么是“垃圾”收集器 3. 如何收集“垃圾” 4. 线程“垃圾”和非线程“垃圾” 5. 弱引用和软引用

1.Java中如何定义一个“垃圾”


所谓垃圾,就是内存中不再有用的对象。

对象成为垃圾的条件:

  • 对于非线程来说,当所有的活动线程(指以及启动但还没有死亡的线程)都不可能访问到该对象时,该对象便成为垃圾。
  • 对于线程对象来说,处理满足上述条件外,还要求此线程本身已经死亡或者还处于新建状态。

光看上面的文字肯定看的模模糊糊,下面我们举例来看看

对于非线程的单个对象,要使其成为垃圾,只需要将指向该对象的所有引用不再指向它即可。下面三种情况都可以实现

  • 将指向该对象的引用设为null
  • 将引用指向别的对象
  • 随着语句块或代码块的退出局部引用消亡
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

// 创建字符串对象,并将引用s指向该对象
String s = new String();
// 将引用s设为null
s = null;
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 创建值为100的Integr对象,并将引用i指向该对象
Integer i = new Integer(100);
// 将引用i指向另一个对象
i = new Integer(200);

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void someFunction() {
    Double d = new Double(26.77);
    System.out.println(d);
}

上面的三段代码分别对应三种情况。 对于非线程的多个对象,我们来看看一段代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

Rubbish a = new Rubbish("孤岛对象1"); 
Rubbish b = new Rubbish("孤岛对象2"); 
Rubbish c = new Rubbish("孤岛对象3"); 
a.brother = b; 
b.brother = c; 
c.brother = a; 
// 断绝环与外界的关系,形成孤岛 
a = null; 
b = null; 
c = null;

上述代码创建了三个Rubbish对象,有三个引用a,b,c分别指向它们,在对象创建完成后,分别让他们的成员brother指向另一个Rubbish对象,最后将三个对象置为null。这个例子告诉我们,有引用指向的对象不一定不是垃圾,关键要看这些对象能不能被活动线程访问到,显然,上述的brother被完全孤立了,所有即使有引用指向它们,也是垃圾。

关于线程对象成为垃圾的情况,我们在后面用代码说明。

2.什么是“垃圾”收集器


垃圾收集器其实就是一个后台守护进程,在内存充足的情况下,它的优先级很低,一般不出来运行,当内存中有很多垃圾时,它的优先级就会变高,并出来清理垃圾,正因为如此,垃圾收集器的运行时间是没有保障的。

我们都知道Java中垃圾收集器是由系统自动运行的,那如果我们程序员自己想要它运行呢?还是有办法的,人还能让尿憋死啊。不过申请了不一定成功。

一般有两种方法申请运行垃圾收集器

  • 使用Runtime类中的gc()方法。
  • 使用System类中的gc()方法。

Runtime的构造方法是private类型的,所以我们不能直接构造Runtime对象,只能通过特定的对象工厂方法来获取该类的对象,可以通过getRuntime()方法获取。而System就不同了,gc()是它的静态方法,我们可以直接进行调用,所以一般我们都使用System.gc();

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

/**
 * @author liu
 * @version 创建时间:2018年3月30日 下午8:11:55
 * 申请垃圾收集器运行
 * 1.可使用Runtime的gc()方法
 * 2.可使用System的静态方法gc()
 * 因为使用Runtime还需要调用getRuntime创建Runtime对象,所有一般用第二种
 */
public class Test01 { 
    public static void main(String[] args) { 
        // 获得当前应用程序的Runtime对象 
        Runtime rt = Runtime.getRuntime(); 
        // 输出当前JVM使用的总内存 
        System.out.println("当前JVM使用的总内存为:" + rt.totalMemory()); 
        // 申请垃圾回收器运行 
        // rt.gc();
        System.gc(); 
        try { 
            // 休眠主线程,提高申请垃圾回收器运行的成功率 
            Thread.sleep(100); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        System.out.println("创建10000000个垃圾对象前JVM可用的内存为:" + rt.freeMemory()); 
        // 创建10000000个rubbish 
        for(int i = 0; i < 10000000; i++) { 
            new String("rubbish"); 
        } 
        System.out.println("创建10000000个rubbish后JVM可用内存:" + rt.freeMemory()); 
        // 申请垃圾回收器运行 
        System.gc(); 
        try { 
            // 休眠主线程,提高申请垃圾回收器运行的成功率 
            Thread.sleep(100); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        // 申请垃圾回收器后可使用的内存 
        System.out.println("申请垃圾回收器后JVM可用内存为:" + rt.freeMemory()); 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
当前JVM使用的总内存为:16252928
创建10000000个垃圾对象前JVM可用的内存为:15739232
创建10000000个rubbish后JVM可用内存:11609984
申请垃圾回收器后JVM可用内存为:15739544

3.如何收集“垃圾”


在Object类中有一个finalize()方法,而Object类又是所有类的直接或间接父类,所以每个类都有一个finalize()方法。所以垃圾在被回收前,都会调用该方法。如果我们要在对象成为垃圾被回收前进行一些操作,就可以重写该方法。

既然能重写finalize()方法,那自然就会涉及到一个问题,如果我们在重写的finalize()方法中添加一些代码,在对象成为垃圾被回收之前,阻止对象被回收,比如把成为垃圾的对象重新指向另一个引用,那么就会造成恶意常驻的现象。

为了避免上述情况的发生,Java规定,在一个对象的生命周期内,finalize()方法只会被调用一次。这是什么意思呢?就是当垃圾收集器要回收垃圾对象之前,调用finalize()方法,如果没有回收成功,那么第二次垃圾收集器回收这个垃圾对象,就不再调用它的finalize()方法,而是直接回收。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

class Father { 
    // 重写finalize 
    public void finalize() throws Throwable { 
        // 调用object类的finalize方法 
        super.finalize(); 
        System.out.println("This is father."); 
    }
}
class Son extends Father { 
    public void finalize() throws Throwable { 
        // 调用父类的finalize方法 
        super.finalize(); 
        System.out.println("This is son."); 
    }
}
public class Test02 { 
    public static void main(String[] args) { 
        // 使该对象成为垃圾对象 
        new Son(); 
        // 申请垃圾回收器 
        System.gc(); 
        try { 
            // 使主线程休眠,提供申请垃圾回收器的成功率 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

This is father.
This is son.

4.线程垃圾和非线程垃圾

前面已经介绍了非线程的对象成为垃圾的情况,但没有用代码说明,现在我们用孤岛现象来描述一下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

class Rubbish {
    // 定义一个自身类型的引用 
    Rubbish brother; 
    // 定义字符串常量 
    String name; 
    // 无参构造函数 
    public Rubbish() {} 
    /**
     * 有参构造函数
     * @param name
     */ 
    public Rubbish(String name) { 
        this.name = name; 
    } 
    /**
     * 重写finalize方法
     */ 
    public void finalize() throws Throwable { 
        System.out.println(this.name + "对象成为垃圾"); 
    }
}
public class Island { 
    public static void main(String[] args) { 
        Rubbish a = new Rubbish("孤岛对象1"); 
        Rubbish b = new Rubbish("孤岛对象2"); 
        Rubbish c = new Rubbish("孤岛对象3"); 
        a.brother = b; 
        b.brother = c; 
        c.brother = a; 
        // 断绝环与外界的关系,形成孤岛 
        a = null; 
        b = null; 
        c = null; 
        // 申请垃圾回收器 
        System.gc(); 
        // 使主线程休眠,提供申请的成功率 
        try { 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
孤岛对象3对象成为垃圾
孤岛对象2对象成为垃圾
孤岛对象1对象成为垃圾

可以看到,孤岛中的对象都被当做垃圾回收了,这说明,“有引用指向的对象就不是垃圾”这句话是错误的。

对于线程来说,除了要满足一般对象成为垃圾的条件之外,还要求线程没有启动或已经死亡,下面我们也用孤岛来描述。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

/**
 * @author liu
 * @version 创建时间:2018年3月31日 上午10:42:27
 * 线程垃圾
 * 执行第一次垃圾收集时,线程a还没有死亡,岛上的对象还可以被a访问,所以没有形成孤岛
 * 执行第二次垃圾收集时,线程a已经死亡,整个孤岛成为垃圾,三个线程都被收集
 */
class RubbishThread extends Thread { 
    RubbishThread brother; 
    String name; 
    /**
     * 无参构造方法
     */ 
    public RubbishThread() {} 
    /**
     * 带参构造方法
     * @param name
     */ 
    public RubbishThread(String name) { 
        this.name = name; 
    } 
    /**
     * 重写run方法,定义线程执行体
     */ 
    public void run() { 
        System.out.println(this.name +"开始执行"); 
        // 对线程进行延迟,使其在足够时间是活着的 
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        System.out.println(this.name + "执行结束"); 
    } 
    /**
     * 重写finalize方法
     */ 
    public void finalize() { 
        System.out.println(this.name + "对象成为垃圾"); 
    }
}
public class Test03 { 
    public static void main(String[] args) { 
        RubbishThread a = new RubbishThread("孤岛线程1"); 
        RubbishThread b = new RubbishThread("孤岛线程2"); 
        RubbishThread c = new RubbishThread("孤岛线程3"); 
        a.brother = b; 
        b.brother = c; 
        c.brother = a; 
        // 启动一个线程 
        a.start(); 
        // 形成孤岛 
        a = null; 
        b = null; 
        c = null; 
        System.out.println("---------对无引用但活着的线程进行垃圾收集----------"); 
        System.gc(); 
        try { 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        // 线程进入死亡状态后申请垃圾收集 
        System.out.println("-------------对无引用并死亡的线程进行垃圾收集----------"); 
        System.gc(); 
        try { 
            Thread.sleep(100); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

---------对无引用但活着的线程进行垃圾收集----------
孤岛线程1开始执行
孤岛线程1执行结束
-------------对无引用并死亡的线程进行垃圾收集----------
孤岛线程3对象成为垃圾
孤岛线程2对象成为垃圾
孤岛线程1对象成为垃圾

5.弱引用和软引用


弱引用

试想一下这种情况,一个对象在使用后就不可避免的会成为垃圾,那如果将来某一个时间我们可还要使用,这可怎么办呢?根据前面的描述,一个对象要么是垃圾,要么就不是垃圾,显然前面的知识已经不足以胜任这项工作了。这时候我们使用弱引用—-Java.lang.ref.WeakReference就可以解决问题。

WeakReference类中常用的一个方法是get(),该方法会返回弱引用指向的普通对象。

Java中还有一个WeakHashMap类—–java.util.WeakHashMap,其用法和HashMap相同,只是其中的键都为弱引用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @author liu
 * @version 创建时间:2018年3月31日 上午11:20:10
 * 弱引用
 * 在对象满足垃圾收集的条件后,还可以通过弱引用对其进行访问,但不影响对象成为垃圾
 * 在没有进行垃圾回收之前,弱引用还可以访问,一旦进行垃圾回收,对象就会被清出内存,此时弱引用就不能访问了。
 */
class MyWeakObject { 
    String name; 
    public MyWeakObject(String name) { 
        this.name = name; 
    } 
    public void finalize() { 
        System.out.println(this.name + "满足垃圾回收的条件"); 
    } 
    public void show() { 
        System.out.println(this.name + "对象还可以被调用"); 
    }
}
public class MyWeakObjectTest { 
    public static void main(String[] args) { 
        System.out.println("----------弱引用对象垃圾收集的情况------------"); 
        MyWeakObject a = new MyWeakObject("MyWeakObject1"); 
        // 让弱引用指向创建的MyWeakObject1对象 
        WeakReference<MyWeakObject> wr = new WeakReference<MyWeakObject>(a); 
        // 使MyWeakObject1对象成为垃圾 
        a = null; 
        // 通过弱引用访问MyWeakObject对象 
        ((MyWeakObject)wr.get()).show(); 
        System.out.println("第一次进行垃圾收集"); 
        // 申请垃圾回收器 System.gc(); 
        try { 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        // 再次通过弱引用访问MyWeakObject对象 
        if(wr.get() != null) { 
            ((MyWeakObject)wr.get()).show(); 
        } 
        System.out.println("--------------弱引用HashMap------------"); 
        // 创建弱引用map对象 
        WeakHashMap<MyWeakObject, String> whm = new WeakHashMap<MyWeakObject, String>(); 
        // 创建MyWeakObject对象 
        MyWeakObject b = new MyWeakObject("MyWeakObject2"); 
        whm.put(b, "xxxxxx"); 
        b = null; 
        // 通过弱引用访问键对象 
        ((MyWeakObject)whm.keySet().iterator().next()).show(); 
        System.out.println("第二次进行垃圾收集"); 
        System.gc(); 
        try { 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

----------弱引用对象垃圾收集的情况------------
MyWeakObject1对象还可以被调用
第一次进行垃圾收集
MyWeakObject1满足垃圾回收的条件
--------------弱引用HashMap------------
MyWeakObject2对象还可以被调用
第二次进行垃圾收集
MyWeakObject2满足垃圾回收的条件

软引用

前面说了,软引用指向的对象在成为垃圾还没有被垃圾收集器收集之前,弱引用还可以对其进行调用,但一旦对象被清理出内存,那么就不能调用了。但有时如果我们想在内存没有耗尽之前,就不要把对象给清理,那应该怎么办呢?

这时可以使用软引用—–java.lang.ref.SoftReference,软引用指向的对象在内存没有被耗尽的情况下不会被垃圾收集器回收,具体规则如下:

  • 在内存耗尽之前,垃圾收集器会尝试释放软引用所指向的对象,但在内存耗尽之前不会对软引用所指向的对象进行垃圾收集。
  • 在对软引用指向的对象进行垃圾收集时,垃圾收集器以最近最少使用的顺序释放软引用指向的对象。

软引用中也有一个常用的方法get(),返回软引用指向的普通对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

/**
 * @author liu
 * @version 创建时间:2018年3月31日 上午11:48:12
 * 软引用
 * 1.无论对象在不计算软引用的情况下是否成为垃圾对象,垃圾收集器在抛出内存耗尽异常之前尝试释放软引用指向的对象
 * 但内存耗尽之前不会对软引用指向的对象进行垃圾收集
 * 2.在对软引用指向的对象进行垃圾收集时,垃圾收集器以最近最少使用的顺序释放引用指向的对象
 */
class MySoftObject { 
    String name; 
    public MySoftObject(String name) { 
        this.name = name; 
    } 
    public void finalize() { 
        System.out.println(this.name + "对象满足垃圾回收的条件"); 
    } 
    public void show() { 
        System.out.println(this.name + "还可以被再次使用"); 
    }
}
public class MySoftObjectTest { 
    public static void main(String[] args) { 
        // 创建一个MySoftObject对象 
        MySoftObject a = new MySoftObject("MySoftObject1"); 
        // 让软引用指向创建的对象 
        SoftReference<MySoftObject> sf = new SoftReference<MySoftObject>(a); 
        // 使对象满足垃圾回收的条件 
        a = null; 
        // 通过软引用调用对象 
        ((MySoftObject)sf.get()).show(); System.gc(); 
        try { 
            Thread.sleep(200); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        // 再次通过软引用访问对象 
        ((MySoftObject)sf.get()).show(); 
        // 耗尽内存 
        int size = 10000000; 
        String[] sa = new String[size]; 
        for(int i = 0; i < size; i++) { 
            sa[i] = new String("Hello world"); 
        } 
    }
}

输出

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
MySoftObject1还可以被再次使用
MySoftObject1还可以被再次使用
MySoftObject1对象满足垃圾回收的条件

可以看到申请垃圾收集器后还可以通过软引用调用对象,只有在内存耗尽后,对象才被清理掉。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-05-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 秃头哥编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Java垃圾收集学习笔记
(1)除了释放不再被引用的对象,垃圾收集器还要处理堆碎块。请求分配新对象时可能不得不增大堆空间的大小,虽然可以使用的空闲空间是足够的,但是堆中没有没有连续的空间放得下新对象。可能会导致虚拟机产生不必要的”内存不足“错误。
全栈程序员站长
2021/04/07
2450
jvm系列之垃圾收集器
   java内存在运行时被分为多个区域,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程生成和销毁;每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,在这几个区域内就不需要过多考虑回收问题,因为方法结束或者线程结束时,内存自然就跟着回收了。而堆区就不一样了,我们只有在程序运行的时候才能知道哪些对象会被创建,这部分内存是动态分配的,垃圾收集器主要关注的也就是这部分内存。
六个核弹
2022/12/23
2080
Java虚拟机之垃圾收集器(5)
(1)Java 内存运行时区域的各个部分,其中程序计数器、虚拟机栈、本地方法栈三个区域随线程而生,随线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。
Java后端技术
2018/08/09
3040
Java虚拟机之垃圾收集器(5)
JVM-04垃圾收集Garbage Collection(上)【垃圾对象的判定】
谈起垃圾收集 (Garbage Collection ,GC),有3个问题是无法回避的
小小工匠
2021/08/17
4590
深入了解JVM垃圾收集器
程序计数器、JVM栈、本地方法栈这三个内存区域和线程是一一对应的,并且每一个线程的这三个区域相互独立互不干扰。他们都随着线程的产生而产生,线程的灭亡而灭亡。JVM栈和本地方法栈中的栈帧都随着方法的加载而入栈,随着方法的结束而出栈。 栈帧的大小是在程序员写类的时候就确定下来的。因此这三种区域的内存大小都具备确定性,当方法结束或线程结束后,这些内存空间就会自动被回收掉,所以JVM无需考虑这些区域的内存回收问题。 堆内存和方法区的内存分配和回收就不一样了,因为一个接口中的多个实现类所需要的内存可能不一样,并且一个
大闲人柴毛毛
2018/03/09
6870
[JVM] 垃圾收集器与内存分配策略
 垃圾收集器与内存分配策略 最早人们思考GC需要完成的3件事情: 哪些内存需要回收 什么时候回收 如何回收 经过发展,内存动态分配和回收技术已经成熟,为什么还要了解GC和内存分配呢? 当需要排查各种内
wOw
2018/09/18
4500
[JVM] 垃圾收集器与内存分配策略
垃圾收集器与内存分配策略
上面示例代码中有两段完全一样的代码片段,执行结果却是一次逃脱成功,一次失败了,这里任何一个finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法就不会再次执行。
程序员波特
2024/01/19
2160
垃圾收集器与内存分配策略
JVM内存与垃圾回收篇第16章垃圾回收相关概念
右边的图:后期有一些对象不用了,按道理应该断开引用,但是存在一些链没有断开,从而导致没有办法被回收。
yuanshuai
2022/08/17
2890
JVM内存与垃圾回收篇第16章垃圾回收相关概念
Java 引用类型简述
强引用 ( Strong Reference ) 强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 ps:强引用其实也就是我们平时A a = new A()这个意思。 强引用特性 强引用可以直接访问目标对象。 强引用所指向的对象在任何时候都不会被系统回收。 强引用可能导致内存泄漏。 Final Reference 当前类是否是
tomas家的小拨浪鼓
2018/06/27
7680
JVM:垃圾收集策略与算法
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事是确定这些对象之中哪些还"存活"着,哪些已经"死去"了。
HLee
2021/02/24
3880
JVM:垃圾收集策略与算法
JVM之垃圾回收相关概念
但是局部变量表中只有一个,其中1位置的buff没有删除,依然占着槽,所以对象引用存在,没有回收
Java微观世界
2025/01/20
980
JVM之垃圾回收相关概念
Java垃圾收集器总结
ZGC、G1和Shenandoah 都是以低延迟为主的收集器,总结了一下三者的区别
eeaters
2022/03/06
4990
Java垃圾收集器总结
深入理解Java虚拟机:垃圾收集
垃圾收集的目的在于清除不再使用的对象。gc通过确定对象是否被活动对象引用来确定是否收集该对象。两种常用的方法是引用计数和对象引用遍历。
全栈程序员站长
2022/08/28
2710
深入理解Java虚拟机:垃圾收集
Java finalize函数与软引用、弱引用、虚引用
       它不是C/C++中的析构函数,而是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协”。也就是说,finalize函数最初被设计的用途是类似于C/C++的析构函数,用于在对象被销毁前最后的内存回收。Java与C/C++的相似性和不同之处在于:在C++中,对象的内存在哪个时刻被回收,是可以明确确定的(假设程序没有缺陷),一旦C++的对象要被回收了,在回收该对象之前对象的析构函数将被调用,在该函数中释放对象占用的内存;在java中,对象的内存在哪个时刻回收,取决于垃圾回收器何时运行,一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法, 并且在下一次垃圾回收动作发生时,才会真正的回收对象占用的内存,由于JVM垃圾回收运行时机是不确定的,因而finalize()的调用具有不确定性。JVM只保证方法会调用,但不保证方法里的任务会被执行完(这块儿可以从Java源码Finalizer.class中得知:在源码中,执行finalize()方法是通过开启一个低优先级的线程来执行的,而finalize()方法在执行过程中的任何异常都会被catch,然后被忽略,因而无法保证finalize方法里的任务会被执行完)。由于执行finalize()的是一个低优先级的线程,既然是一个新的线程,虽然优先级低了点,但也是和垃圾收集器并发执行的,所以垃圾收集器没必要等这个低优先级的线程执行完才继续执行。也就是说,有可能会出现对象被回收之后,那个低优先级的线程才执行finalize()方法。
saintyyu
2021/11/22
8680
深入理解 JVM 之——垃圾回收与内存分配策略
内存回收的时机是由垃圾回收器(Garbage Collector)来决定的,而垃圾回收器的具体策略和时机会根据不同的实现而有所差异。一般情况下,以下几种情况会触发内存回收:
浪漫主义狗
2023/09/06
8820
深入理解 JVM 之——垃圾回收与内存分配策略
JVM的垃圾收集器策略
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
大大大大大先生
2018/09/04
3390
Java底层:GC相关
光有垃圾标记算法还不行,JVM还需要有垃圾回收算法来将这些标记为垃圾的对象给释放回收掉。主要的回收算法有以下几种:
端碗吹水
2020/09/23
5830
Java底层:GC相关
JVM性能优化系列-(2) 垃圾收集器与内存分配策略
在堆里存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,首要的就是确定这些对象中哪些还“存活”着,哪些已经“死去”(即不可能再被任何途径使用的对象)。
码老思
2023/10/19
4280
JVM性能优化系列-(2) 垃圾收集器与内存分配策略
JVM中常用的垃圾收集器和收集算法(超详解G1收集器)
主要为对象, 而提到对象, 我们需要知道对象什么时候被回收? 主要是引用失效的时候, 那什么时候引用失效, 下面就要讲讲对象的四种引用了
天下之猴
2024/09/20
6271
JVM中常用的垃圾收集器和收集算法(超详解G1收集器)
JAVA 垃圾收集器与内存分配策略
垃圾收集技术并不是Java语言首创的,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言。垃圾收集技术需要考虑的三个问题是:
zhangheng
2020/04/28
5970
相关推荐
Java垃圾收集学习笔记
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档