强引用、软引用、弱引用、虚引用及方法区的垃圾回收策略

以下按照引用强弱关系进行排序:

  • 强引用

内存溢出都不会回收。

  • 软引用
public class MyDataTest {
    public static void main(String[] args) {
        //SoftReference<String[]> data=new SoftReference<>(new String[1024*1024*500]);
        String[] data=new String[1024*1024*500];
        System.out.println(data);
        MyDataTest.drainMemory();
        System.out.println(data);
    }

    public static void drainMemory(){
        int[] str=new int[1024*1024*500];
    }

public class MyDataTest {
    public static void main(String[] args) {
        SoftReference<String[]> data=new SoftReference<>(new String[1024*1024*500]);
        System.out.println(data.get());
        MyDataTest.drainMemory();
        System.out.println(data.get());
    }

    public static void drainMemory(){
        int[] str=new int[1024*1024*500];
    }
}

内存不足时回收软引用对象,如果内存还是不足,才会抛出内存溢出异常。

使用场景:像这种如果内存充足,GC时就保留,内存不够,GC再来收集的功能很适合用在==缓存==的引用场景中。在使用缓存时有一个原则,如果缓存中有就从缓存获取,如果没有就从数据库中获取,缓存的存在是为了加快计算速度,如果因为缓存导致了内存不足进而整个程序崩溃,那就得不偿失了。

  • 弱引用
public class MyDataTest {
    public static void main(String[] args) {
        MyDataTest.weakRef();
    }

    public static void weakRef() {
        WeakReference<MyData> data=new WeakReference<>(new MyData());
        System.gc();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(data.get());
    }
}
public class MyData {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
}
 

 

GC即回收。

使用场景:ThreadLocal等,防止内存泄漏。

  • 虚引用

一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

 

  • finalize方法

宣告一个对象的灭亡需要经历两次被标记的过程:如果对象在进行可达性分析时发现其不在与GC Roots相连接的引用链上,则会被进行第一次标记且进行一次筛选,筛选的条件时是否有必要执行finalize()方法,如果已执行过或对象没有覆写finalize(),则认为“没有必要执行”。如果对象被判定为有必要执行,则会将对象加入F-Queue中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的执行是虚拟机会触发这个方法但不承诺会等待它运行结束。因为如果一个对象在finalize()中执行缓慢或者发生了死循环,将会导致F-Queue队列中的其他对象处于永久等待状态,甚至导致整个内存回收系统崩溃。稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己--只要重新与引用链上的任何一个对象建立关联即可,那在二次标记时它将被移出“即将回收的集合”。反之,则被回收。

 

上述代码中第一次逃脱成功、第二次却失败了,因为finalize()方法只会被执行一次,第二次自动失效。

  • 回收方法区

在gc过程中,对永久代的垃圾回收主要包含两部分内容:废弃常量和无用的类。

常量:例如一个字符串“abc”已经进入字符串常量池但是当前没有一个String引用常量池中的“abc”,则回收它。

类:类的回收条件比较苛刻,包含以下三点:Java堆中不存在该类的任何实例,均已被回收;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类的方法。

 

以上!!!

 

 

 

 
posted @ 2020-08-03 17:15  机械公敌  阅读(463)  评论(0编辑  收藏  举报