Java对引用的定义

无论是通用引用计数算法判断对象的引用数据,还是通过可达性分析算法判断对象的引用链是否可达,判定对象是否存活都与“引用”有关。在JDK1.2之前,Java中的引用定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存中,如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。很多系统的缓存功能都符合这样的应用场景。

在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用、软引用、弱引用、虚引用4种,这4种引用强度依次逐渐减弱。强引用就是指在程序代码中普遍存在的,类似“Object obj=new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。

Java中根据其生命周期的长短,将引用分为4类。

强引用

特点:我们平常典型编码Object obj = new Object()中的obj就是强引用。通过关键字new创建的对象所关联的引用就是强引用。 当JVM内存空间不足,JVM宁愿抛出OutOfMemoryError运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。

软引用

特点:软引用通过SoftReference类实现。 软引用的生命周期比强引用短一些。只有当JVM认为内存不足时,才会去试图回收软引用指向的对象,即JVM会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。

应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

弱引用

弱引用通过WeakReference类实现,弱引用的生命周期比软引用短。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

应用场景:弱应用同样可用于内存敏感的缓存。

虚引用

特点:虚引用也叫幻象引用,通过PhantomReference类来实现。无法通过虚引用访问对象的任何属性或函数。幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);

程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动。

应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知。

关于强引用之外的另外三大引用,可以用以下代码来测试

public class TestReference {private static List<Object> list = new ArrayList<>();public static int M = 1024 * 1024;public static void main(String[] args) {testSoftReference();System.out.println("------------");list.clear();testWeakReference();System.out.println("------------");testPhantomReference();}private static void testSoftReference() {Runtime runtime = Runtime.getRuntime();String value = "我是软引用";SoftReference<String> sfRefer = new SoftReference<>(value);//可以获得引用对象值System.out.println(sfRefer.get());for (int i = 0; i < 10; i++) {byte[] buff = new byte[M];SoftReference<byte[]> sr = new SoftReference<>(buff);list.add(sr);}System.out.println(runtime.freeMemory() / M + "M(free) / " + runtime.maxMemory() / M + "M(max)");//主动触发垃圾回收System.gc();//垃圾回收后,再查看数据for (int i = 0; i < list.size(); i++){Object obj = ((SoftReference) list.get(i)).get();System.out.println(obj);}//再申请4MB的空间,触发垃圾回收SoftReference<Object> softReference = new SoftReference<Object>(new byte[4 * M]);System.out.println("softReference.get() : " + softReference.get());for (int i = 0; i < list.size(); i++){Object obj = ((SoftReference) list.get(i)).get();System.out.println(obj);}}private static void testWeakReference() {Runtime runtime = Runtime.getRuntime();String value = "我是弱引用";WeakReference<String> wkRefer = new WeakReference<>(value);//可以获得引用对象值System.out.println(wkRefer.get());//创建10MB数组for (int i = 0; i < 10; i++) {byte[] buff = new byte[M];WeakReference<byte[]> sr = new WeakReference<>(buff);list.add(sr);}System.out.println(runtime.freeMemory() / M + "M(free) / " + runtime.maxMemory() / M + "M(max)");//主动触发垃圾回收System.gc();//垃圾回收后,再查看数据for(int i = 0; i < list.size(); i++){Object obj = ((WeakReference) list.get(i)).get();System.out.println(obj);}}private static void testPhantomReference() {// 创建一个字符串对象String str = new String("Java的4大引用");// 创建一个引用队列ReferenceQueue rq = new ReferenceQueue();// 创建一个虚引用,让此虚引用引用到"疯狂Java讲义"字符串PhantomReference pr = new PhantomReference (str , rq);// 切断str引用和"Java的4大引用"字符串之间的引用str = null;// 取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象,所以此处输出nullSystem.out.println(pr.get());// 强制垃圾回收System.gc();System.runFinalization();// 垃圾回收之后,虚引用将被放入引用队列中// 取出引用队列中最先进入队列中的引用与pr进行比较System.out.println(rq.poll() == pr);}
}

在笔记本上运行效果如下,可以看到默认的JVM可用内存很大,垃圾回收时在内存空间足够用的情况下,软引用对象占用的空间不会被回收。

我是软引用
231M(free) / 3641M(max)
[B@60e53b93
[B@5e2de80c
[B@1d44bcfa
[B@266474c2
[B@6f94fa3e
[B@5e481248
[B@66d3c617
[B@63947c6b
[B@2b193f2d
[B@355da254
softReference.get() : [B@4dc63996
[B@60e53b93
[B@5e2de80c
[B@1d44bcfa
[B@266474c2
[B@6f94fa3e
[B@5e481248
[B@66d3c617
[B@63947c6b
[B@2b193f2d
[B@355da254
------------
我是弱引用
219M(free) / 3641M(max)
null
null
null
null
null
null
null
null
null
null
------------
null
trueProcess finished with exit code 0

现在设置-Xms2M -Xmx15M,然后重新运行,效果如下:

我是软引用
1M(free) / 14M(max)
[B@60e53b93
[B@5e2de80c
[B@1d44bcfa
[B@266474c2
[B@6f94fa3e
[B@5e481248
[B@66d3c617
[B@63947c6b
[B@2b193f2d
[B@355da254
softReference.get() : [B@4dc63996
null
null
null
null
null
null
null
null
null
null
------------
我是弱引用
2M(free) / 14M(max)
null
null
null
null
null
null
null
null
null
null
------------
null
trueProcess finished with exit code 0

软引用的实际应用

如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

比如在图片加载框架中,通过软引用来实现内存缓存。

//实现图片异步加载的类
public class AsyncImageLoader {//以Url为键,SoftReference类型为值,建立缓存HashMap键值对。private Map<String, SoftReference<Drawable>> mImageCache = new HashMap<String, SoftReference<Drawable>>();//实现图片异步加载public Drawable loadDrawable(final String imageUrl, final ImageCallback callback) {//查询缓存,查看当前需要下载的图片是否在缓存中if(mImageCache.containsKey(imageUrl)) {SoftReference<Drawable> softReference = mImageCache.get(imageUrl);if (softReference.get() != null) {return softReference.get();}}final Handler handler = new Handler() {@Overridepublic void dispatchMessage(Message msg) {//回调ImageCallbackImpl中的imageLoad方法,在主线(UI线程)中执行。callback.imageLoad((Drawable)msg.obj);}};/*若缓存中没有,新开辟一个线程,用于进行从网络上下载图片,* 然后将获取到的Drawable发送到Handler中处理,通过回调实现在UI线程中显示获取的图片*/new Thread() {      public void run() {Drawable drawable = loadImageFromUrl(imageUrl);//将得到的图片存放到缓存中mImageCache.put(imageUrl, new SoftReference<Drawable>(drawable));Message message = handler.obtainMessage(0, drawable);handler.sendMessage(message);};}.start();//若缓存中不存在,将从网上下载显示完成后,此处返回null;return null;}//定义一个回调接口public interface ImageCallback {void imageLoad(Drawable drawable);}//通过Url从网上获取图片Drawable对象;protected Drawable loadImageFromUrl(String imageUrl) {try {return Drawable.createFromStream(new URL(imageUrl).openStream(),"debug");} catch (Exception e) {// TODO: handle exceptionthrow new RuntimeException(e);}}
}

Java中的四大引用相关推荐

  1. 理解Java中的弱引用(Weak Reference)

    理解Java中的弱引用(Weak Reference) 本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限, ...

  2. java的弱引用_深入理解Java中的弱引用

    不久之前,我面试了一些求职Java高级开发工程师的应聘者.我常常会面试他们说,"你能给我介绍一些Java中得弱引用吗?",如果面试者这样说,"嗯,是不是垃圾回收有关的?& ...

  3. java弱引用怎么手动释放,十分钟理解Java中的弱引用,十分钟java引用

    十分钟理解Java中的弱引用,十分钟java引用 本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,帮助大家理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限 ...

  4. Java中对象和引用的理解

    2019独角兽企业重金招聘Python工程师标准>>> 偶然想起Java中对象和引用的基本概念,为了加深下对此的理解和认识,特地整理一下相关的知识点,通过具体实例从两者的概念和区别两 ...

  5. java的弱引用_理解Java中的弱引用(Weak Reference)

    本篇文章尝试从What.Why.How这三个角度来探索Java中的弱引用,理解Java中弱引用的定义.基本使用场景和使用方法.由于个人水平有限,叙述中难免存在不准确或是不清晰的地方,希望大家可以指出, ...

  6. java中数组的引用是什么意思_java数组的引用有什么意义

    java中的数组引用就是让数组b直接指向数组a(即b = a;).在java编程中这样做的目的是为了提高程序运行的效率. 试想一下,假如数组中有上万个元素,在拷贝数组时,如果将数组a的所有元素都一一拷 ...

  7. JAVA中的各种引用

    JAVA中引用的分类 强引用 软引用 表示一个有用但是非必须的对象. 弱引用 表示希望在下一次垃圾回收时回收的对象 虚引用 主要用于监控对象何时被回收 实现 Reference Reference是软 ...

  8. java中的持有引用

    Thinking in java对这个地方说得不清楚,随便在网上找了几篇文章也没看太明白. 是说想要在以后访问某个对像,但现在又想先回收了它,就可以使用持有引用来实现,而问题是回收就是回收了,怎么才能 ...

  9. java中数组的引用是什么意思_java中的数组是引用数据类型。

    [判断题]合金的组元决定了合金的性能 . [单选题]下列分子中存在分子内氢键的是 [单选题]下列电子构型中,不属于基态原子电子构型的是 [判断题]面心立方晶格的纯铁较体心立方晶格的纯铁强度高 [判断题 ...

最新文章

  1. 速来!亚马逊云科技AI盛会开源专场吹响集结号
  2. 参数 ByVal 和 ByRef 区别
  3. R-apply()函数
  4. 工信部:“5G+工业互联网”在建项目全国已超2000余个
  5. android 微信支付测试,求大神指导一下!!!android开发,测试demo按步骤配置,无法调用到微信支付...
  6. Q2 Spring Boot自动配置原理(ok)
  7. 【linux高级程序设计】(第八章)进程管理与程序开发 4
  8. Android手机分辨率基础知识(DPI,DIP计算)(转)
  9. webpack配置工程师(一):基本篇
  10. ubuntu 12.04 下安装 PyTesser 进行OCR识别 - 从波 - 博客园
  11. python如何实现清屏
  12. 机器学习实战练手项目
  13. PC端如何跟手机端兼容
  14. java web 页面布局框架_jsp框架(jsp实现页面框架布局)
  15. PhotoShop纸张大小
  16. 山西省忻州市水泥厂能耗监测系统的设计与应用
  17. 匹配,为什么要“共轭”
  18. 奇葩问题☞ npm install 报错 gyp ERR
  19. 加拿大LMIA劳工批文有效期延长至18个月,为海外劳工在加拿大临时工作及移民创造便利条件
  20. AcWing 138. 兔子与兔子

热门文章

  1. FFmpeg_note
  2. 淘宝店铺名称怎么修改
  3. 在nodejs addon 环境下抓视频和音频数据包
  4. Android 禁止息屏 保持屏幕常亮的方式
  5. Ubuntu18.04 python环境搭建 pycharm+anaconda3+Pyqt5
  6. Python赚钱新姿势兼职私活月入8000+,分享几个额外创收渠道
  7. Unity VR游戏开发干货教程:VR中的交互方式
  8. EXCHANGE 完全访问权限邮箱的设置
  9. Java关于word转pdf工具方法的几种解决方案和我遇到一些问题(html中转、jacob、Docx4j)
  10. 2018 完美世界校招笔试编程题(Java)