你需要知道的四种java引用

栏目: Java · 发布时间: 5年前

内容简介:在Java语言中,除了原始数据类型(int/float等基本数据类型)的变量,其他所有的引用类型,都是指向到内存中不同的对象(一般为new操作符创建的对象)。而为了更好的管理内存,java在1.2版本中增加了包java.lang.ref,这个包提供了三个引用对象的实现类:SoftReference、WeakReference、PhantomReference。即软引用、弱引用、虚引用。此外对于直接用new操作符创建的对象,如果该对象使用一个变量来引用,那么这种引用类型称为强引用。本篇文章将从对象的生命周期、

Java 语言中,除了原始数据类型(int/float等基本数据类型)的变量,其他所有的引用类型,都是指向到内存中不同的对象(一般为new操作符创建的对象)。而为了更好的管理内存,java在1.2版本中增加了包java.lang.ref,这个包提供了三个引用对象的实现类:SoftReference、WeakReference、PhantomReference。即软引用、弱引用、虚引用。此外对于直接用new操作符创建的对象,如果该对象使用一个变量来引用,那么这种引用类型称为强引用。本篇文章将从对象的生命周期、GC以及Object#finalize()方法说起,认识一下这三种引用对象的作用和应用场景。

对象的生命周期及GC

为了说明强引用、软引用、弱引用、虚引用,我们有必要来了解一下对象的生命周期和GC的基本原理。下面就以一个不十分详细的说明来描述一下。(对于想深入了解的可以参见书籍《深入理解Java虚拟书 》这本书)

假设我们有一段代码

public static void foo(String bar) {
    Integer baz = new Integer(bar);
}
// 调用函数
foo("123")

传入参数bar给方法foo(String bar),该方法内部创建了一个Integer的对象。

下图描述了在java内存空间中,栈和堆的关系。可以看出,bar作为方法foo的入参,其栈内存放了一个变量bar,指向堆中的地址,字符串”123”在堆内存中的地址。在foo函数的执行栈中,baz变量同样在栈中存放了指向堆内存,即Integer实例的内存地址。

重点:在foo函数执行完毕后,弹出执行栈,其内部的baz、bar变量立即销毁,此时堆中的”123”、Integer(“123”)对象将没有任何引用,当JVM内存不足,触发GC时,这些内存空间将被回收。

你需要知道的四种java引用

梳理一下,对于一般情况(也可以理解为强引用的情况下),堆内存中的对象,当没有变量引用时,在GC周期它们所属的内存将被回收。但在软引用、弱引用的作用下,就算堆内存中的对象被引用类型的实例(即SoftReference或WeakReference)引用,仍然可以触发回收动作。

四种引用的官方介绍

引用官方的说法 ,在GC对实例进行可达性分析时,主要有四种可达性:

  1. strongly reachable,如果某个对象可以在不遍历任何引用对象的情况下到达某个对象,则该对象是strongly reachable
  2. softly reachable,如果某个对象无法通过strongly reachable,需要通过soft reference引用对象才可达,则该对象是soft reachable
  3. weakly reachable, 如果某个对象无法通过strongly、softly reachable,而只能通过weak reference引用对象才可达,则该对象是weakly-reachable,
    当清除weakly-reachable对象时,其中包含的实例将被清除
  4. phantom reachable,无法通过strongly、softly、weakly reachable可达的,则它要么是已经被finalize机制回收或者phantom reference实例引用着

四种引用对象提供了一种有限的能力用于跟GC打交道,一个程序可以使用一个引用对象来维护某个其他对象的引用,使得后者对象仍然可以在某种情况下被收集器来回收。这样讲,可能有点不明确,请看图和描述。

你需要知道的四种java引用

在引用对象(reference-object)中使用了一个实例变量引用了一个对象(referents),在这种情况下就算引用对象(reference-object)仍然处于作用域中,被引用对象仍然有可能被GC回收。各自的作用如官网所说:

你需要知道的四种java引用

软引用的应用场景

软引用,在JVM内存不足时,将回收软引用对象中被引用的对象。

应用场景:处理超大数据集时,避免OOM

public static List<List<Object>> processResults(ResultSet rslt)
throws SQLException
{
    try
    {
        // 创建一个软引用对象,其中引用了一个LinkedList实例,用于保存处理结果
        SoftReference<List<List<Object>>> ref
        = new SoftReference<List<List<Object>>>(new LinkedList<List<Object>>());

        ResultSetMetaData meta = rslt.getMetaData();
        int colCount = meta.getColumnCount();

        int rowCount=0;
        while (rslt.next()){
            rowCount++;
            // store the row data

            List<List<Object>> results = ref.get();
            if (results == null)
                throw new TooManyResultsException(rowCount);
            else
                results.add(row);
            // 取消强引用,这样才能保证在内存不足时可以进行软引用的收集动作
            results = null;
        }

        return results;
    }
    finally
    {
        closeQuietly(rslt);
    }
}

弱引用应用场景

弱引用,当一个对象只存在弱引用时,在GC时将被回收

应用场景:

  1. 关联没有固有关系的对象
    // 固定关系的一端
    static class Person {
    
        // 当发生GC时,回收实例将调用该方法
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Finalize");
            super.finalize();
        }
    }
    
private static void test1() throws InterruptedException {
    // 使用WeakHashMap来绑定两个对象的关系
    Map<Person, String> personIds = new WeakHashMap<>();
    Person person = new Person();
    // WeakHashMap会将key包装为一个WeakReference对象
    personIds.put(person, "1");

    String s = personIds.get(person);
    System.out.println(s);

    // 去掉对实例的强引用之后,当前实例只存在弱引用,即WeakHashMap中对应的key
    person = null;
    // 手动触发GC,将会回收person实例
    // 如果使用HashMap来保存关系的话,为了避免内存泄漏,我们需要手动HashMap#remove(person)
    // 来删除引用,让GC回收person。使用弱引用后,将无需我们来管理这一部分内存管理
    System.gc();

    TimeUnit.SECONDS.sleep(10);
}
  1. 通过规范化的Map来减少重复
    // 模拟String类的intern(String string)方法,来保证不会有重复对象
    private Map<String,WeakReference<String>> _map
        = new WeakHashMap<String,WeakReference<String>>();
    
    public synchronized String intern(String str)
    {
        WeakReference<String> ref = _map.get(str);
        String s2 = (ref != null) ? ref.get() : null;
        if (s2 != null)
            return s2;
    
        _map.put(str, new WeakReference(str));
        return str;
    }
    

虚引用的应用场景

虚引用(PhantomReference)不同于软引用、弱引用,其无法获得被引用对象,但是可以在引用对象被回收时,通过ReferenceQueue将自身(引用对象本身)来通知应用程序,进行一些回收资源的操作。

收到回收资源的操作,Java中有一个类似于C++的析构函数(Object#finalize)。如果在类中覆盖该方法,那么在GC回收该类的实例的时候,将触发调用该方法(GC通过一个线程来调用,时间复杂度为O(n)级别),在执行完这个方法之后,才会进行内存资源的回收。因此存在一个弊端,如果很多类都实现了finalize方法,那么会导致内存资源回收失败,发生OOM异常。

总结

  1. 四种引用:强引用、软引用、弱引用、虚引用

    2. 软引用、弱引用、虚引用在Java中各自的实现类,以及作用

    熟悉这四种引用的含义和作用,将可以帮助我们更好的了解和使用JVM内存,避免内存泄漏导致的OOM异常

参考


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

High Performance JavaScript

High Performance JavaScript

Nicholas C. Zakas / O'Reilly Media / 2010-4-2 / USD 34.99

If you're like most developers, you rely heavily on JavaScript to build interactive and quick-responding web applications. The problem is that all of those lines of JavaScript code can slow down your ......一起来看看 《High Performance JavaScript》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器