欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

5.9 j(java学习笔记)强软弱虚引用及WeakHashMap、IdentityHashMap、EnumMap

程序员文章站 2022-04-15 11:04:40
一、引用分类 强:运行垃圾回收机制后也不回收,程序出现内存溢出也不回收。 软:在垃圾回收机制运行时判断内存是否已满,如果内存已满则回收,内存充足则不回收。 弱:垃圾回收机制运行后不论内存是否充足都会立即回收。 虚:虚引用和没有引用一样,必须配合引用队列使用。 我们来看例子: 我们看上述结果: 强引用 ......

一、引用分类

强:运行垃圾回收机制后也不回收,程序出现内存溢出也不回收。

软:在垃圾回收机制运行时判断内存是否已满,如果内存已满则回收,内存充足则不回收。

弱:垃圾回收机制运行后不论内存是否充足都会立即回收。

虚:虚引用和没有引用一样,必须配合引用队列使用。

 

我们来看例子:

import java.lang.ref.phantomreference;
import java.lang.ref.reference;
import java.lang.ref.referencequeue;
import java.lang.ref.softreference;
import java.lang.ref.weakreference;
import java.util.hashmap;
import java.util.iterator;
import java.util.map;
import java.util.map.entry;
import java.util.set;

public class test{
    final map<string,softreference> m = new hashmap<>();
    public static void main(string args[]){
        //创建一个新的弱引用,引用给定的对象
        string strong = new string("strong");//strong对"strong"的强引用
        string soft = new string("soft");//soft对"soft"的强引用
        string weak = new string("weak");//weak对"weak"的强引用
        string phantom = new string("phantom");//phantom对"phantom"的强引用
        referencequeue<string> queue = new referencequeue<string>();//引用队列
        reference<string> restrong = new softreference<>(strong);
        reference<string> resoft = new softreference<>(soft);//resoft对"soft"的软引用
        reference<string> reweak = new weakreference<>(weak);//reweak对"weak"的弱引用
        reference<string> rephantom = new phantomreference<>(phantom,queue);//rephantom指向"phantom"的虚引用
        soft = null;//断开soft对"soft"的强引用
        weak = null;//断开weak对"weak"的强引用
        phantom = null;//断开phantom对"phantom"的虚引用
        //断开强引用后,就只剩下resoft对"soft"的软引用,reweak对"weak"的弱引用,rephantom对"phantom"的虚引用
        system.out.println("strong运行gc前:"+strong);//强引用对象
        system.out.println("soft运行gc前:"+resoft.get());//获取当前软引用的对象
        system.out.println("weak运行gc前:"+reweak.get());//获取当前弱引用的对象
        system.out.println("phantom运行gc前:"+rephantom.get());//获取当前虚引用的对象
        system.gc();//运行垃圾回收
        system.out.println("-----------------------");
        system.out.println("strong运行gc后:"+strong);//强引用对象
        system.out.println("soft运行gc后:"+resoft.get());//获取当前软引用的对象
        system.out.println("weak运行gc后:"+reweak.get());//获取当前弱引用的对象
        system.out.println("phantom运行gc后:"+rephantom.get());//获取当前虚引用的对象
    }
}

 

运行结果:
strong运行gc前:strong soft运行gc前:soft weak运行gc前:weak phantom运行gc前:null ----------------------- strong运行gc后:strong soft运行gc后:soft weak运行gc后:null phantom运行gc后:null

我们看上述结果:

强引用在运行垃圾回收后也不会被回收,软引用在运行垃圾回收后,如果内存足够则不回收,如果内存不足则回收。

弱引用在运行垃圾回收后,不论内存是否充足都会对其回收。虚引用就可没有引用一样,无论何时都会被回收。

 

一般像date time = new date();这种都是强引用,也是我们平常使用最多的,只要强引用存在,gc运行后不会对其回收。

我们先看上列代码中的软引用所引用,一开始soft对象对“soft”是一个强引用,然后resoft对“soft”是一个软引用,

之后soft对象指向null,即断开了对“soft”的强引用,此时只剩下resoft对“soft”的软引用。

再来分析弱引用,一开始weak对“weak”的强引用,然后是reweak对“weak”的一个弱引用,

之后weak对象指向nul,即断开了对“weak”的强引用,此时只剩下reweak对“weak”的弱引用。

最后后还有一个虚引用phantom。

 

下面画个图来看下:

 5.9 j(java学习笔记)强软弱虚引用及WeakHashMap、IdentityHashMap、EnumMap

还有一点需要说明:

我们先来看下列代码:

import java.lang.ref.phantomreference;
import java.lang.ref.reference;
import java.lang.ref.referencequeue;
import java.lang.ref.softreference;
import java.lang.ref.weakreference;
import java.util.hashmap;
import java.util.iterator;
import java.util.map;
import java.util.map.entry;
import java.util.set;

public class test{
    final map<string,softreference> m = new hashmap<>();
    public static void main(string args[]){
        string weak = "weak";//weak对"weak"的强引用
        reference<string> reweak = new weakreference<>(weak);//reweak对"weak"的弱引用
        weak = null;//断开weak对"weak"的强引用
        system.out.println("weak运行gc前:"+reweak.get());//获取当前弱引用的对象
        system.gc();//运行垃圾回收
        system.out.println("-----------------------");
        system.out.println("weak运行gc后:"+reweak.get());//获取当前弱引用的对象
    }
}
运行结果:
weak运行gc前:weak
-----------------------
weak运行gc后:weak

看到这个可能会有疑问,不是只有一个弱引用执行"weak"吗,运行gc后应该会被回收呀?

大家注意new string("weak")和“weak”是不一样的,前者新建一个对象时是放在堆中,而后者是一个字符串常量是放在静态区的。

而静态区的对象是不会被清理的,所以即使只有弱引用指向“weak”,但由于“weak”不会被清理,所以弱引用依然指向"weak"。

 

对于需要经常使用的内容我们可以采用软引用,这样在需要使用时既不会被回收,也不会出现内存溢出(oom)错误,很适合做为缓存。

当一些只需要少量使用较大数据时,我们可以采用弱引用,防止其占用内存。

这些可以根据具体使用情况优化引用关系。

这里有一个别人举得使用软引用优化内存溢出的例子:

例子出自:

//首先定义一个hashmap,保存软引用对象。
private map<string, softreference<bitmap>> imagecache = new hashmap<string, softreference<bitmap>>(); public void addbitmaptocache(string path) { // 强引用的bitmap对象 bitmap bitmap = bitmapfactory.decodefile(path);//此方法被执行完,bitmap会被清理,此时只保留下了imagecache中对 // 软引用的bitmap对象                 //bitmapfactory.decodefile(path)的软引用,要是实在不放心就对bitmap置null.             softreference<bitmap> softbitmap = new softreference<bitmap>(bitmap); // 将软引用对象放到到map中使其缓存 imagecache.put(path, softbitmap); } public bitmap getbitmapbypath(string path) { // 从map中取软引用的value(bitmap)对象 softreference<bitmap> softbitmap = imagecache.get(path); // 判断当前软引用对象是否被清理 if (softbitmap == null) { return null; } // 如果被清理返回null,反之返回改对象 bitmap bitmap = softbitmap.get(); return bitmap; }

 

二.weakhashmap

理解了上列内容,就很容易理解weakhashmap了。

weakhashmap的键是弱引用,回收键后删除key-value对象。

import java.util.iterator;
import java.util.map;
import java.util.set;
import java.util.weakhashmap;

public class testweakhashmap {

    public static void main(string[] args) {
        // todo auto-generated method stub
        map<string ,string> m = new weakhashmap<>();
        m.put(new string("1"), "一");
        m.put(new string("2"), new string("二"));
        m.put("3", new string("三"));
        m.put(new string("4"), new string("四"));
        system.gc();
        set<map.entry<string,string>> s_m = m.entryset();
        iterator<map.entry<string,string>>ite = s_m.iterator();
        while(ite.hasnext()){
            map.entry<string, string> en = ite.next();
            system.out.println("ket:" + en.getkey() + "\t" + "value:" + en.getvalue());
        }
    }
}
运行结果:
ket:3    value:三

运行gc后弱引用的键都被回收了,但“3”在静态区不会被回收,所以弱引用仍然可以指向“三”。

 

三、identityhashmap

identityhashmap是比较键的地址去重,如果键的地址相同就代表同一个对象,如果键的地址不同就表示不同对象。

 而hashmap是使用hashcode和equals去重。

import java.util.identityhashmap;
import java.util.map;

public class testidentityhashmap {

    public static void main(string[] args) {
        // todo auto-generated method stub
        map<string,string> m = new identityhashmap<>();
        m.put("1", "1");//保留
        m.put("1", "1");//去除
        m.put(new string("1"), "1");//保留
        m.put(new string("1"), "1");//保留
        system.out.println(m.size());
    }

}
运行结果:
3

第一个“1”是字符串常量,存放在静态区是共享的,第一个和第二个“1”都是指向同一个存放在静态区的常量“1”。所以第二个放置时地址相同会被舍弃。

new string("1"),是在堆中开辟一块存放“1”的地址,无论该值是否存在,只要new了就会开辟一个新的空间。

我们来看一个图:

5.9 j(java学习笔记)强软弱虚引用及WeakHashMap、IdentityHashMap、EnumMap

 

四.enummap

键(key)必须为枚举的值,构造方法中必须为指定枚举类。

import java.util.enummap;
import java.util.iterator;
import java.util.map;
import java.util.map.entry;
import java.util.set;

public class testenummap {
    public static void main(string args[]){
        map<season,string> m = new enummap<>(season.class);//构造方法指定枚举类
        m.put(season.spring, "春困");
        m.put(season.summer, "夏乏");
        m.put(season.autumn, "秋无力");
        m.put(season.winter, "冬日正好眠");
        set<entry<season, string>> s_m = m.entryset();
        iterator<entry<season, string>>ite = s_m.iterator();
        while(ite.hasnext()){
            entry<season, string> en = ite.next();
            system.out.println(en.getvalue());
        }
    }
}

enum season{
    spring,summer,autumn,winter
}
运行结果:
春困
夏乏
秋无力
冬日正好眠