一、引用分类

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

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

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

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

我们来看例子:

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)错误,很适合做为缓存。

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

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

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

例子出自:https://blog.csdn.net/arui319/article/details

//首先定义一个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");//覆盖前一个的value "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
}
运行结果:
春困
夏乏
秋无力
冬日正好眠

5.9 j(java学习笔记)强软弱虚引用及WeakHashMap、IdentityHashMap、EnumMap的更多相关文章

  1. ThreadLocal以及强软弱虚引用

    1.ThreadLocal ThreadLocal即线程本地,可以实现每个线程存入取出TreadLocal值互不影响.因为TheadLocal底层是用了一个Map结构存放数据,而这个Map是从当前这个 ...

  2. java强软弱虚引用详解(转载)

    转载自:http://zhangjunhd.blog.51cto.com/113473/53092/ ava:对象的强.软.弱和虚引用 2007-12-01 17:20:20 标签:Java 软引用  ...

  3. Java学习笔记之深入理解引用

    引言:Java中数据传递的方式,除了基本数据类型是按照值传递,其它类型全部是按照引用传递,这和C++有很大区别,但是很多网上文章都解释的不清楚,甚至是错误的,在查阅资料之后,下面整理出一个比较容易理解 ...

  4. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

  5. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  6. 20145231第二周Java学习笔记

    20145231 <Java程序设计>第2周学习总结 教材学习内容总结 本周的学习采用的依然是先看课本,再看视频,然后实践敲代码,最后根据学习笔记总结完成博客. 第三章:基础语法 知识点比 ...

  7. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  8. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  9. Java学习笔记31(IO:Properties类)

    Properties类,表示一个持久的j集,可以存在流中,或者从流中加载 是Hashtable的子类 map集合的方法都能用 用途之一:在开发项目中,我们最后交给客户的是一个编译过的class文件,客 ...

随机推荐

  1. eclipse关闭错误警告提示

  2. TCP ------ TCP四次挥手(断开连接)及断开过程

    1.正常情况下,调用close(),产生的其中一个效果就是发送FIN,只有双方都调用close(),才会出现正常的四次挥手. 2.如果是服务器,发起四次挥手是在关闭accept()返回的套接字,而不是 ...

  3. Java之戳中痛点 - (2)取余用偶判断,不要用奇判断

    取余判断原则:取余用偶判断,不要用奇判断 先看一个 程序: package com.test; import java.util.Scanner; public class t1 { public s ...

  4. ES6学习笔记(四)—— async 函数

    await 是 async wait 的简写, 是 generator 函数的语法糖. async 函数的特点: async 声明一个方法是异步的,await 则等待这个异步方法执行的完成 async ...

  5. Linux 之test expr命令

    test指令(使用指令man查询) 功能:检查文件类型,值比较. test的各种参数和使用. test EXPRESSION1 –a EXPRESSION2 当表达式1和表达式2同时为真时值为真 te ...

  6. 在SDK中使用Ubuntu仿真器

    Ubuntu仿真器是开发过程中非常有用,尤其是在没有任何Ubuntu设备时.在将仿真器附加到SDK后,你便可以在上面运行程序,安装点击数据包,等等,类似在物理设备上的操作一样. 在这里,您可以了解如何 ...

  7. C++11 自动释放锁(转)

    原文转自 https://blog.csdn.net/lmb1612977696/article/details/77712170 c++11加入了很多新的特性,值得我们去探索. 先看一个例子:普通的 ...

  8. mvn常用的构建命令

    mvn -v 查看maven版本 mvn compile 编译 mvn test 测试 mvn package 打包 mvn clean 删除target mvn install 安装jar包到本地仓 ...

  9. import详解

    试想一下 在工作中今年在一个项目中可能会导入某一个目录下的模块文件,那这个时候怎么样才能让Python解释器能够找到该模块进行调用呢? - 将这个模块拷贝一份到当前使用目录下. 这种方式让模块太冗余 ...

  10. 超详细saltstack安装部署及应用

    1.环境准备 准备两台虚拟机 主机名 ip role linux-node1 10.0.0.7 master linux-node2 10.0.0.8 minion 在节点1上安装 master 和 ...