垃圾收集器与内存分配策略

一、对象是否已死

引用计数算法:

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。它实现简单,判定效率高,但是很难解决对象间相互引用的问题。

public class Test {
    
    public Object instance = null;
    private static final int _1mb = 1024 * 1024;
    /** 该成员变量唯一的意义就是占点内存,以便能在GC日志中看清楚是否被回收过*/
    private byte[] bigSize = new byte[2 * _1mb];
    
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Test objA = new Test();// objA引用+1 1
        Test objB = new Test();// objB引用+1 1
        objA.instance = objB;// objB引用+1 2
        objB.instance = objA;// objA引用+1 2
        
        objA = null;// objA引用-1 1
        objB = null;// objB引用-1 1
        
        // 如果使用引用计数算法,此时objA和objB都不会被回收,然而objA和objB都已经不能被访问
        System.gc();
    }
}

可达性算法:

通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜搜所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

在java中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
  2. 方法区中类静态属性引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI(即一般说的native方法)引用的对象。

再谈引用:

  1. 强引用:"Object obj = new Object()",这类引用只要还存在,就不会被回收.
  2. 软引用:对于软引用关联的对象,在系统将要发生内存溢出异常以前,将会把这些对象列进回收范围中进行第二次回收。jdk1.2以后提供了SoftReference类来实现软引用。可以做缓存。
  3. 弱引用:只要jvm发现了弱引用,就会将之回收。jdk1.2之后提供了WeakReference类来实现弱引用。可以使用在某个对象被回收,其对应的一些数据也会被回收的场景。(具体使用还没掌握)。
  4. 虚引用:也被称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。jdk1.2之后提供了PhantomReference类来实现虚引用。

生存还是死亡:

真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法(没必要的直接回收)。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。

如果这个对象被判定为有必要执行finalize方法,那么这个对象将会放置在一个叫做F-Queue的队列中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行finalize方法。

public class Test {
    
    private String name;
    public static Test MY_TEST = null;
    
    public Test(String name) {
        this.name = name;
    }
    
    public void isAlive() {
        System.out.println("我是" + this.name + ",我还活着!");
    }
    
    public String getName() {
        return this.name;
    }
    
    @Override
    public void finalize() throws Throwable {
        super.finalize();
        Test.MY_TEST = this;
        System.out.println("复活方法被执行");
    }
    
    public static void main(String[] args) throws InterruptedException {
        Test.MY_TEST = new Test("小明");
        
        Test.MY_TEST = null;
        System.gc();
        Thread.sleep(500);
        // 我是小明,我还活着!
        if (null != Test.MY_TEST) {
            Test.MY_TEST.isAlive();
        } else {
            System.out.println("oh no! i am dead! :(");
        }
        
        Test.MY_TEST = null;
        System.gc();
        Thread.sleep(500);
        // oh no! i am dead! :(
        if (null != Test.MY_TEST) {
            System.out.println(Test.MY_TEST.getName());
        } else {
            System.out.println("oh no! i am dead! :(");
        }
    }
}

以上结果可以看出test的finalize方法确实被调用过,并且该方法只会被系统自动调用一次。

而实际上,finalize这个方法运行代价高,不确定性大,不推荐使用。能用finalize执行做的工作,都能用try-finally做得很好。

 

回收方法区:

主要是回收两部分:废弃常量(常量池已经移到堆中)和无用的类。

废弃常量,例如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫“abc”的,换句话说,就是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果这个时候发生内存回收,而且必要的话,这个“abc”就会被系统清理出常量池。

无用的类,类需要同时满足下面三个条件才能算是“无用的类”:

  • 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
  • 加载该类的ClassLoader已经被回收。
  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

 

二、垃圾收集算法

1、标记清除算法

2、复制算法

3、标记整理算法

4、分代收集算法

现在的虚拟机一般用的是分代收集算法,新生代存活对象一般比较少,使用复制算法。老生代使用标记清除或者标记整理算法。

 

三、垃圾收集器

cms收集器:

cms是一款以获取最短回收停顿时间为目标的收集器。

cms主要作用于老生代(此时新生代一般使用serial或者parnew收集器配合cms收集器)。

cms收集器主要分为四个阶段:

1、初始标记(stw)

2、并发标记

3、重新标记(stw)

4、并发清除

初始标记主要标记GC Roots连接的以及对象以及新生代引用的老生代对象。

并发标记主要标记初始标记标记的对象的连接链的所有可达对象。

重新标记标记以上两步未标记的GC Roots连接的以及对象以及新生代引用的老生代对象和这些对象连接链的所有可达对象(这些对象都是并发标记时,应用线程新产生的)。

并发清除时,个人理解是内存里会存有一个指针,这个指针指向重新标记以后。

以上黑色的为标记的存活对象,指针左边的空白为需要清除的对象。而新来的对象则从指针右边开始分配内存。

// TODO

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值