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 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。
下面画个图来看下:
还有一点需要说明:
我们先来看下列代码:
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了就会开辟一个新的空间。
我们来看一个图:
四.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 }
运行结果: 春困 夏乏 秋无力 冬日正好眠