java引用计数法、可达性分析法、强软虚弱、强引用、软引用、弱引用、虚引用、回收方法区、finalize()方法


判断对象是否为垃圾(是否存活)的算法

1.引用计数算法 (基本上废掉了)
2.可达性分析算法


3.2.1 引用计数算法

https://blog.csdn.net/linsongbin1/article/details/51448714

对象中添加一个引用计数器,当有引用指向这个对象时,引用计数器+1,当指向该对象的引用失效时,引用计数器-1。

任何时刻计数器为0 的对象就是不可能再被使用的;

Person p = new Person();则引用计数器+1
p = null;引用p失效,则引用计数器-1

优点
1.实时性,无需等到内存不够的时候,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收。
2.区域性 ,更新对象的计数器时,只是影响到该对象,不会扫描全部对象

缺点
1.浪费资源,每次对象被引用时,即使内存足够,都需要去更新计数器;
2.无法解决循环引用问题(现在主流的JVM很少使用引用计数算法的原因),因为如果存在对象间相互引用,如果这个指向对象的引用为null,则这几块对象内存会被gc认为是垃圾

循环引用

class TestA{
  public TestB b;

}
class TestB{
  public TestA a;
}
public class Main{
    public static void main(String[] args){
        A a = new A();
        B b = new B();
        a.b=b;
        b.a=a;
        a = null;
        b = null;
        
        //假设在这行发生GC,objA和objB不能被回收
        System.gc();
    }
}

3.2.2 可达性分析算法(重点)

在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。

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

在这里插入图片描述

    上图的对象object5,object6,object7虽然有互相引用,但它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。

作为GC Roots的对象有:

1 虚拟机栈(栈桢中的 局部变量表中的 本地变量表)中的引用的对象
2 方法区中的 类的静态属性 引用的对象(static)
3 方法区中的常量引用的对象(final修饰的变量)
4 本地方法栈中JNI(Native方法)的引用的对象


3.2.3 再谈引用

强引用

就是在程序代码之中普遍存在的,类似Object obj = new Object() 这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象;

当JVM的内存空间不足时,宁愿抛出OutOfMemoryError使得程序异常终止也不愿意回收具有强引用的存活着的对象!

软引用

用来描述一些还有用但并非必须的元素。当内存空间足够的时候,垃圾回收器不会回收它,只有当JVM认定内存空间不足时才会去回收软引用指向的对象;

    JVM会确保在抛出OOM前清理软引用指向的对象,而且会尽可能优先回收长时间闲置不用的软引用指向的对象,对那些刚构建的或刚使用过的软引用指向的对象尽可能的保留软引用是通过SoftReference类实现的.get()方法获取对象。

    软引用一般用来实现内存敏感的缓存,如果有空闲内存就可以保留缓存,当内存不足时就清理掉,这样就保证使用缓存的同时不会耗尽内存。例如图片缓存框架中缓存图片就是通过软引用的

//mObject 对 MyObject 的强引用
MyObject mObject = new MyObject();
//来自 SoftReference对象 的软引用
SoftReference mSoftReference = new SoftReference(mObject);
            
//结束 mObject 对MyObject的强引用这时候MyObject为软引用,如果这时候执行GC,MyObject对象可能会被回。
mObject = null;
            
//获取MyObject对象,如果这之前执行GC,MyObject对象就可能会被回收,如果被回收,则get()返回null。
mSoftReference.get();

以下的了解
    使用ReferenceQueue清除已经失去了软引用对象的SoftReference:
    如果有很多软引用的对象被回收之后,就会存在大量的SoftReference对象(比如上面的mSoftReference),这些对象的遗留没有任何用处,却可能会带来内存泄露。所以,软引用通常可以和一个引用队列(ReferenceQueue)联合使用;如果软引用所引用的对象被垃圾回收,java虚拟机就会把这个软引用加入到与之关联的引用队列(ReferenceQueue)中。
    所以在创建软引用的对象时:

MyObject mObject = new MyObject();
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(mObject,queue);
mObject = null;

弱引用

用来描述非必须对象的,但是它的强度比软引用更弱一些,无论当前内存是否足够都会回收掉只被弱引用关联的对象

    垃圾回收器会扫描它所管辖的内存区域的过程中,只要发现弱引用的对象,不管内存空间是否有空闲,都会立刻回收它。
    弱引用是通过WeakReference类实现的,也是通过get()方法获取对象。

Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;

wf.get();

//返回是否被垃圾回收器标记为即将回收的垃圾
wf.isEnQueued();

虚引用

虚引用的唯一目的在于跟踪垃圾回收过程,在对象被收集器回收时收到一个系统通知

    所以可以利用虚引用来进行销毁前的一些操作,比如说资源释放等。

Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;

//永远返回null,没有意义,了解
pf.get();

//返回是否从内存中已经删除,唯一作用
pf.isEnQueued();

3.2.4 生存还是死亡

在可达性算法中,一个不可达的对象也并非非死不可

若要被真正的回收需要经历两次标记:

  • 第一次标记:
        如果对象在可达性分析中没有与GC Roots相连接的引用链,那么此时就会被第一次标记并且进行一次筛选

    筛选的条件是是否++有必要执行finalize()方法++。当对象没有覆盖finalize()方法或者该方法已经被虚拟机调用过,那么就认为是没必要执行finalize()方法。

  • 第二次标记:
        如果该对象++有必要执行finalize()方法++,那么这个对象将会放在一个称为F-Queue的队列中;稍后GC对处于F-Queue中的对象进行第二次被标记,如果对象在finalize()中重新与引用链上任何一个对象建立关联即可(如把当前对象的引用this赋值给某对象的类变量/成员变量,重新建立可达的引用),否则,该对象将被移除“即将回收”集合,等待回收。

  • 关于finalize()方法
        finalize()只会在对象内存回收前被调用一次;
        finalize()方法现在并没有什么用,finalize()是Java刚诞生时为了使C/C++程序员更容易接受它所做出的一个妥协。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2YYxz86-1602329911723)(DBA57715FA6A4F088544215129F22127)]

上面的总结如下:
如何自我救赎:
1.对象覆写了finalize()方法(这样在被判死后才会调用此方法,才有机会做最后的救赎);
2.在finalize()方法中重新引用到"GC Roots"链上(如把当前对象的引用this赋值给某对象的类变量/成员变量,重新建立可达的引用).


3.2.5 回收方法区

方法区(永久代)的垃圾收集主要回收两部分内容:废弃常量无用的类

  • 假如一个字符串“abc”已经进入了常量池中,如果当前系统没有任何Stirng对象引用常量池的“abc”常量,这个时候发生内存回收并且有必要的话,这个常量就会被清理出常量池。

  • 常量池中的其他类(接口)、方法、字段的符号引用也与此类似。

判断类是无用的类,必须同时满足以下3个要求:
1.这个类的所有对象都被回收了,就是,堆中没有这个类的实例;
2.加载这个类的classloader已经被回收;
3.这个类的java.lang.class没有被任何地方引用,无法再任何地方通过反射访问该类的方法。

方法区、元空间和永久代的概念

  1. 方法区也被称为永久代是因为HotSpot设计团队希望将GC分带手机扩展到方法区;

  2. HotSpot虚拟机在1.8之后已经取消了永久代,改为元空间;

可以把元空间,永久代都理解成方法区;


猜你喜欢

转载自blog.csdn.net/qq_43369986/article/details/109003089