美文网首页
垃圾收集器与内存分配策略(一)

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

作者: jqdywolf | 来源:发表于2018-03-31 17:28 被阅读0次

    垃圾收集器与内存分配策略之--对象已死吗

    GC关注的问题其实就是三个:

    • 哪些内存可以被回收
    • 何时回收
    • 如何回收

    所有的GC问题都是关于这三点的描述。
    对于JVM来说,首先线程独有的三块(程序计数器、栈、本地方法栈)都是随着线程的开始而创建,线程结束而消亡,一个栈帧在开始时内存已经固定,所以GC没必要处理这部分内存。我们所说的GC都是针对堆和方法区而言的。

    对象已死吗

    如何判断一个对象已死?一般主流两种方法:引用计数法和可达性分析法

    引用计数法

    很简单,每个对象维护一个引用计数器,当某个引用指向这个对象时,就让这个引用计数器加1。当一个对象的引用计数器为0,说明对象已死。
    致命的缺点:
    不好解决循环引用问题。

    可达性分析法

    通过一组“GC Root”的对象作为起始点,向下搜索,走过的路径上的每个对象是还活着的。不可达的对象就是死的。HotSpot用的就是这个方法。
    Java中可被作为GC Root的对象有四种:

    • 栈中引用指向的对象
    • 方法区中static引用指向的对象
    • 方法区中常量引用指向的对象
    • 本地方法栈中引用指向的对象

    再谈引用

    一般我们说的引用都是指强引用。

    Object obj = new Object();//obj就是一个强引用
    

    而Java中引入一些“弱”的引用。目的为了引入一种这样的对象:当内存足够时,对象保留内存中,当内存不够时,这些对象可被回收。
    Java里引用分为四种:强引用、软引用、弱引用、虚引用。引用强度逐渐减弱。
    这四种引用的区别就是指向的对象的生存时间不一样。

    • 强引用
      这个就是我们普通说的引用,如果一个对象存在这种引用。即时内存要溢出,这些对象也不能被回收。
    • 软引用
      在要发生内存溢出之前,会先将这些引用指向的对象回收调。(其中这种说法不对,应该是一个对象只有软引用指向它时)。
      比如我们定义一个软引用:
    SoftReference<String> s = new SoftReference<String>(new String("123"));
    
    • 弱引用
      生命周期为下一次GC之前。无论内存是否足够,这些指向的对象都会被回收。
    WeakReference<String> w = new WeakReference<String>(new String("234"));
    
    • 虚引用
      一个对象是否有虚引用和它的生命周期毫无关系。虚引用的唯一目的就是在对象被回收的时候会受到一个系统通知。
    ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
    PhantomReference<String> p = new PhantomReference<>(new String("434"), referenceQueue);//当对象被回收时,referenceQueue就会受到一个消息
    

    一般软引用和弱引用都用于本地缓存中,每次用时判断一下是否被回收,如果被回收再去到数据库里捞。

    SoftReference<String> s = new SoftReference<String>(new String("123"));
    if (null == s.get()) {//如果已被回收
        //到数据库里捞
    }else {//如果没被回收
        System.out.println(s.get());//直接使用
    }
    

    生存还是死亡

    一个对象GC Root不可达并不代表着立刻死亡。一个对象的死亡要经历两次标记。
    具体过程如下:


    对象死亡过程
    • 当GC Root不可达之后被标记一次,
    • 然后判断这个对象是否需要执行finalize方法。
      判断不需要的标准是:对象没有重写finalize方法或finalize已被虚拟机执行过。[也就是说对象finalize只能被执行一次]
    • 如果判断需要执行,则将这个对象丢到F-Queue中,然后虚拟机会有一个Finalize线程去消费执行对象的finalize方法,但并不会等待执行结束。如果在执行finalize方法中[在被标记第二次之前]又把此对象关联到GC Root上,这个对象就又会活过来。【注意这种现象一个对象只能出现一次,因为finalize方法只可能会执行一次】
    • 所以说一个对象的finalize被执行了,这个对象可能还是活着的。且finalize方法被强烈建议不要使用,这个方法不会有人保证它是否会执行、何时执行、何时执行结束。

    回收方法区

    回收方法区的性价比很低,且Java虚拟机规范中并没有强制要求要回收方法区。
    回头方法区主要回收两点:常量和无用的类。

    • 常量
      这里说的就是常量池中常量,比如字面量和符号引用。
      判断是否应该回收比较简单,比如字面量:系统中没人叫这个东西了,自然就可以被回收了。
    • 无用的类
      这个判断起来就比较麻烦了。
      主要由三点:
      该类的实例已全部被回收
      加载该类的ClassLoader已被回收
      该类的Class对象无引用且无法使用反射来调用
      满足这三点是可以被回收,但也不是一定就会被回收。可以使用-Xnoclassgc来控制。

    相关文章

      网友评论

          本文标题:垃圾收集器与内存分配策略(一)

          本文链接:https://www.haomeiwen.com/subject/abggcftx.html