`
jiasanshou
  • 浏览: 16801 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java GC 相关的4种引用

 
阅读更多

1. Strong Reference

StrongReference 是 Java 的默认引用实现,它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时Java GC 执行后将会被回收

  1. @Test
  2. publicvoidstrongReference(){
  3. Objectreferent=newObject();
  4. /**
  5. *通过赋值创建StrongReference
  6. */
  7. ObjectstrongReference=referent;
  8. assertSame(referent,strongReference);
  9. referent=null;
  10. System.gc();
  11. /**
  12. *StrongReference在GC后不会被回收
  13. */
  14. assertNotNull(strongReference);
  15. }

2. WeakReference & WeakHashMap

WeakReference, 顾名思义,是一个弱引用,当所引用的对象在 JVM 内不再有强引用时, Java GC 后 weak reference 将会被自动回收

  1. @Test
  2. publicvoidweakReference(){
  3. Objectreferent=newObject();
  4. WeakReference<Object>weakRerference=newWeakReference<Object>(referent);
  5. assertSame(referent,weakRerference.get());
  6. referent=null;
  7. System.gc();
  8. /**
  9. *一旦没有指向referent的强引用,weakreference在GC后会被自动回收
  10. */
  11. assertNull(weakRerference.get());
  12. }

WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在Java GC 后将自动删除相关的 entry

  1. @Test
  2. publicvoidweakHashMap()throwsInterruptedException{
  3. Map<Object,Object>weakHashMap=newWeakHashMap<Object,Object>();
  4. Objectkey=newObject();
  5. Objectvalue=newObject();
  6. weakHashMap.put(key,value);
  7. assertTrue(weakHashMap.containsValue(value));
  8. key=null;
  9. System.gc();
  10. /**
  11. *等待无效entries进入ReferenceQueue以便下一次调用getTable时被清理
  12. */
  13. Thread.sleep(1000);
  14. /**
  15. *一旦没有指向key的强引用,WeakHashMap在GC后将自动删除相关的entry
  16. */
  17. assertFalse(weakHashMap.containsValue(value));
  18. }

3. SoftReference

SoftReference 于 WeakReference 的特性基本一致, 最大的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用

  1. @Test
  2. publicvoidsoftReference(){
  3. Objectreferent=newObject();
  4. SoftReference<Object>softRerference=newSoftReference<Object>(referent);
  5. assertNotNull(softRerference.get());
  6. referent=null;
  7. System.gc();
  8. /**
  9. *softreferences只有在jvmOutOfMemory之前才会被回收,所以它非常适合缓存应用
  10. */
  11. assertNotNull(softRerference.get());
  12. }

4. PhantomReference

作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同,因为它的 get() 方法永远返回 null, 这也正是它名字的由来

  1. @Test
  2. publicvoidphantomReferenceAlwaysNull(){
  3. Objectreferent=newObject();
  4. PhantomReference<Object>phantomReference=newPhantomReference<Object>(referent,newReferenceQueue<Object>());
  5. /**
  6. *phantomreference的get方法永远返回null
  7. */
  8. assertNull(phantomReference.get());
  9. }

诸位可能要问, 一个永远返回 null 的 reference 要来何用,请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference & SoftReference 也可以有这个参数),
PhantomReference 唯一的用处就是跟踪 referent何时被 enqueue 到 ReferenceQueue 中.

5. RererenceQueue

当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.

  1. @Test
  2. publicvoidreferenceQueue()throwsInterruptedException{
  3. Objectreferent=newObject();
  4. ReferenceQueue<Object>referenceQueue=newReferenceQueue<Object>();
  5. WeakReference<Object>weakReference=newWeakReference<Object>(referent,referenceQueue);
  6. assertFalse(weakReference.isEnqueued());
  7. Reference<?extendsObject>polled=referenceQueue.poll();
  8. assertNull(polled);
  9. referent=null;
  10. System.gc();
  11. assertTrue(weakReference.isEnqueued());
  12. Reference<?extendsObject>removed=referenceQueue.remove();
  13. assertNotNull(removed);
  14. }


6.PhantomReferencevs WeakReference

PhantomReference有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC,XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).

其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中,但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,这将导致这一轮的 GC 无法回收这个对象并有可能
引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,也就不会出现上述问题,当然这是一个很极端的例子, 一般不会出现.

7. 对比

Soft vs Weak vs Phantom References Type Purpose Use When GCed Implementing Class
Strong Reference An ordinary reference. Keeps objects alive as long as they are referenced. normal reference. Any object not pointed to can be reclaimed. default
Soft Reference Keeps objects alive provided there’s enough memory. to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key. After a first gc pass, the JVM decides it still needs to reclaim more space. java.lang.ref.SoftReference
Weak Reference Keeps objects alive only while they’re in use (reachable) by clients. Containers that automatically delete objects no longer in use. After gc determines the object is only weakly reachable java.lang.ref.WeakReference
java.util.WeakHashMap
Phantom Reference Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize()) Special clean up processing After finalization. java.lang.ref.PhantomReference

GC、 Reference 与 ReferenceQueue 的交互

A、GC无法删除存在强引用的对象的内存。
B、GC发现一个只有软引用的对象内存,那么:
SoftReference对象的 referent 域被设置为 null ,从而使该对象不再引用 heap 对象。
SoftReference引用过的 heap 对象被声明为 finalizable
当heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放, SoftReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
C、GC发现一个只有弱引用的对象内存,那么:
WeakReference对象的 referent 域被设置为 null,从而使该对象不再引用heap 对象。
WeakReference引用过的 heap 对象被声明为 finalizable 。
当heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放时, WeakReference 对象就被添加到它的 ReferenceQueue (如果后者存在的话)。
D、GC发现一个只有虚引用的对象内存,那么:
PhantomReference引用过的 heap 对象被声明为 finalizable 。
PhantomReference在堆对象被释放之前就被添加到它的 ReferenceQueue 。
值得注意的地方有以下几点:
1、 GC 在一般情况下不会发现软引用的内存对象,只有在内存明显不足的时候才会发现并释放软引用对象的内存。
2、 GC 对弱引用的发现和释放也不是立即的,有时需要重复几次 GC ,才会发现并释放弱引用的内存对象。
3、软引用和弱引用在添加到 ReferenceQueue 的时候,其指向真实内存的引用已经被置为空了,相关的内存也已经被释放掉了。而虚引用在添加到 ReferenceQueue 的时候,内存还没有释放,仍然可以对其进行访问。
代码示例
通过以上的介绍,相信您对Java 的引用机制以及几种引用方式的异同已经有了一定了解。光是概念,可能过于抽象,下面我们通过一个例子来演示如何在代码中使用 Reference 机制。
  1. Stringstr=newString("hello");//①
  2. ReferenceQueue<String>rq=newReferenceQueue<String>();//②
  3. WeakReference<String>wf=newWeakReference<String>(str,rq);//③
  4. str=null;//④取消"hello"对象的强引用
  5. Stringstr1=wf.get();//⑤假如"hello"对象没有被回收,str1引用"hello"对象
  6. //假如"hello"对象没有被回收,rq.poll()返回null
  7. Reference<?extendsString>ref=rq.poll();//⑥

在以上代码中,注意⑤⑥两处地方。假如“hello ”对象没有被回收 wf.get() 将返回“ hello ”字符串对象, rq.poll() 返回 null ;而加入“ hello ”对象已经被回收了,那么 wf.get() 返回 null , rq.poll() 返回 Reference 对象,但是此 Reference 对象中已经没有 str 对象的引用了 ( PhantomReference 则与WeakReference 、 SoftReference 不同 )。

引用机制与复杂数据结构的联合应用

了解了GC机制、引用机制,并配合上ReferenceQueue,我们就可以实现一些防止内存溢出的复杂数据类型。

例如,SoftReference具有构建Cache系统的特质,因此我们可以结合哈希表实现一个简单的缓存系统。这样既能保证能够尽可能多的缓存信息,又可以保证Java虚拟机不会因为内存泄露而抛出OutOfMemoryError。这种缓存机制特别适合于内存对象生命周期长,且生成内存对象的耗时比较长的情况,例如缓存列表封面图片等。对于一些生命周期较长,但是生成内存对象开销不大的情况,使用WeakReference能够达到更好的内存管理的效果。

SoftHashmap的源码一份,相信看过之后,大家会对Reference机制的应用有更深入的理解。

  1. packagecom.***.widget;
  2. //:SoftHashMap.java
  3. importjava.util.*;
  4. importjava.lang.ref.*;
  5. importandroid.util.Log;
  6. publicclassSoftHashMapextendsAbstractMap{
  7. /**TheinternalHashMapthatwillholdtheSoftReference.*/
  8. privatefinalMaphash=newHashMap();
  9. /**Thenumberof"hard"referencestoholdinternally.*/
  10. privatefinalintHARD_SIZE;
  11. /**TheFIFOlistofhardreferences,orderoflastaccess.*/
  12. privatefinalLinkedListhardCache=newLinkedList();
  13. /**ReferencequeueforclearedSoftReferenceobjects.*/
  14. privateReferenceQueuequeue=newReferenceQueue();
  15. //StrongReferencenumber
  16. publicSoftHashMap(){this(100);}
  17. publicSoftHashMap(inthardSize){HARD_SIZE=hardSize;}
  18. publicObjectget(Objectkey){
  19. Objectresult=null;
  20. //WegettheSoftReferencerepresentedbythatkey
  21. SoftReferencesoft_ref=(SoftReference)hash.get(key);
  22. if(soft_ref!=null){
  23. //FromtheSoftReferencewegetthevalue,whichcanbe
  24. //nullifitwasnotinthemap,oritwasremovedin
  25. //theprocessQueue()methoddefinedbelow
  26. result=soft_ref.get();
  27. if(result==null){
  28. //Ifthevaluehasbeengarbagecollected,removethe
  29. //entryfromtheHashMap.
  30. hash.remove(key);
  31. }else{
  32. //Wenowaddthisobjecttothebeginningofthehard
  33. //referencequeue.Onereferencecanoccurmorethan
  34. //once,becauselookupsoftheFIFOqueueareslow,so
  35. //wedon'twanttosearchthroughiteachtimetoremove
  36. //duplicates.
  37. //keeprecentuseobjectinmemory
  38. hardCache.addFirst(result);
  39. if(hardCache.size()>HARD_SIZE){
  40. //RemovethelastentryiflistlongerthanHARD_SIZE
  41. hardCache.removeLast();
  42. }
  43. }
  44. }
  45. returnresult;
  46. }
  47. /**WedefineourownsubclassofSoftReferencewhichcontains
  48. notonlythevaluebutalsothekeytomakeiteasiertofind
  49. theentryintheHashMapafterit'sbeengarbagecollected.*/
  50. privatestaticclassSoftValueextendsSoftReference{
  51. privatefinalObjectkey;//alwaysmakedatamemberfinal
  52. /**Didyouknowthatanouterclasscanaccessprivatedata
  53. membersandmethodsofaninnerclass?Ididn'tknowthat!
  54. Ithoughtitwasonlytheinnerclasswhocouldaccessthe
  55. outerclass'sprivateinformation.Anouterclasscanalso
  56. accessprivatemembersofaninnerclassinsideitsinner
  57. class.*/
  58. privateSoftValue(Objectk,Objectkey,ReferenceQueueq){
  59. super(k,q);
  60. this.key=key;
  61. }
  62. }
  63. /**HerewegothroughtheReferenceQueueandremovegarbage
  64. collectedSoftValueobjectsfromtheHashMapbylookingthem
  65. upusingtheSoftValue.keydatamember.*/
  66. publicvoidprocessQueue(){
  67. SoftValuesv;
  68. while((sv=(SoftValue)queue.poll())!=null){
  69. if(sv.get()==null){
  70. Log.e("processQueue","null");
  71. }else{
  72. Log.e("processQueue","Notnull");
  73. }
  74. hash.remove(sv.key);//wecanaccessprivatedata!
  75. Log.e("SoftHashMap","release"+sv.key);
  76. }
  77. }
  78. /**Hereweputthekey,valuepairintotheHashMapusing
  79. aSoftValueobject.*/
  80. publicObjectput(Objectkey,Objectvalue){
  81. processQueue();//throwoutgarbagecollectedvaluesfirst
  82. Log.e("SoftHashMap","putinto"+key);
  83. returnhash.put(key,newSoftValue(value,key,queue));
  84. }
  85. publicObjectremove(Objectkey){
  86. processQueue();//throwoutgarbagecollectedvaluesfirst
  87. returnhash.remove(key);
  88. }
  89. publicvoidclear(){
  90. hardCache.clear();
  91. processQueue();//throwoutgarbagecollectedvalues
  92. hash.clear();
  93. }
  94. publicintsize(){
  95. processQueue();//throwoutgarbagecollectedvaluesfirst
  96. returnhash.size();
  97. }
  98. publicSetentrySet(){
  99. //no,no,youmayNOTdothat!!!GRRR
  100. thrownewUnsupportedOperationException();
  101. }
  102. }


Java GC小结
一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解Java GC 的工作原理以及性能调优有一定帮助, 在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.

原文链接:http://henryyang.iteye.com/blog/1188328


分享到:
评论

相关推荐

    Java GC的过程

    Java垃圾收集(Garbage Collection, 简称GC)是Java编程中的一项重要特性,它自动管理内存,释放不再使用的对象,避免了程序员手动管理内存可能导致的内存泄露问题。本篇将深入探讨Java中的GC过程。 一、Java内存...

    Java_GC垃圾回收调优指南

    ### Java GC垃圾回收调优指南 #### 概述 在Java开发过程中,垃圾回收(Garbage Collection, GC)是管理内存资源的关键技术之一。合理的GC配置可以显著提高应用程序的性能和稳定性。本指南旨在帮助开发者深入理解...

    Java基础[Java基础]--Java GC工作原理

    ### Java GC工作原理详解 #### 一、引言 在现代软件开发中,Java作为一种广泛使用的编程语言,其强大的功能和高效性深受开发者喜爱。Java的自动内存管理机制是其一大亮点,尤其体现在垃圾收集(Garbage Collection...

    Java程序员必须了解的GC工作原理

    Java程序员在进行内存管理时,垃圾收集(Garbage Collection, GC)是一个不可或缺的部分。理解GC的工作原理对于优化程序性能、防止内存泄漏以及提高系统稳定性至关重要。这篇文章将深入探讨Java中的垃圾收集机制,...

    成为JavaGC专家PartII-如何监控Java垃圾回收

    Java GC的主要目标是识别并清理不再被程序引用的对象,以便有效地利用内存资源。它分为多个阶段,包括标记、扫描、整理等,以确保内存的高效利用。监控GC可以帮助我们理解应用的内存行为,识别潜在的问题,并优化...

    Java 内存区域和GC机制

    Java内存区域和垃圾收集(GC)机制是Java编程中至关重要的一部分,它关乎程序的性能、稳定性和资源管理。本文将深入探讨Java虚拟机(JVM)中的内存划分、垃圾收集的工作原理以及相关工具的使用。 1. **Java内存区域...

    Java GC 专题

    Java垃圾收集(GC)专题针对的是Java编程语言中内存管理机制的重要组成部分。Java自1996年推出以来,其最大的改进之一就是引入了受管内存。在Java之前,C和C++语言中的内存泄漏问题十分常见,这导致开发稳定和安全的...

    JAVA GC 与 JVM调优1

    GC主要针对的是Java堆内存中的对象,这里的对象是由Java栈中的引用指向的。Java栈中每个线程都有一个独立的虚拟机栈,用于存储局部变量表、操作数栈、动态链接和方法出口等信息。当对象不再有任何引用链(GC Roots)...

    Java GC的副本.pptx

    【Java GC机制详解】 Java垃圾回收(Garbage Collection, GC)是Java虚拟机(JVM)的核心特性之一,它负责自动管理程序运行时的内存,有效地避免了内存泄露和溢出的问题。Java GC机制主要涉及以下几个关键方面: 1...

    java垃圾回收(gc)机制详解

    GC主要基于两种概念:引用计数和可达性分析。 1. 引用计数:为每个对象维护一个引用计数,当引用数为0时,对象被视为垃圾。但这种方法无法处理循环引用的问题。 2. 可达性分析:从一组称为“根”(如栈上的本地...

    java垃圾回收(gc)机制详解.pdf

    在JDK1.2之后,Java对引用的概念进行了扩展,提出了四种引用状态:强引用、软引用、弱引用、虚引用,这四种引用类型对对象的垃圾回收有不同的影响。 1. 强引用 强引用是最传统的引用,类似于"Object obj = new ...

    Java GC笔记总纲1

    #### 1.4 Java的四种引用类型 - **强引用**:默认情况下所有的引用都是强引用,除非显式转换为其他类型。 - **软引用**:在内存不足时,会回收软引用关联的对象。 - **弱引用**:在垃圾收集时,无论内存是否充足,...

    Java垃圾回收GC机制

    Java垃圾回收GC机制是Java虚拟机(JVM)中的一种自动内存管理机制,它主要是为了解决Java程序中的内存问题。GC机制的出现使得Java程序员不再需要手动管理内存,提高了开发效率和程序的稳定性。 GC机制的实现基于分...

    java gc调优

    7. **使用弱引用、软引用和虚引用**:这些引用类型可以帮助GC更有效地管理内存。 8. **代码优化**:避免创建不必要的对象,减少临时变量的使用,优化数据结构。 在《浅谈JVM内存管理》的PPT中,可能包含了对上述...

    java gc

    本篇将深入探讨Java中的垃圾收集机制,包括其原理、工作流程、调优策略以及相关工具的使用。 首先,我们要理解Java内存模型中的几个区域:堆(Heap)、栈(Stack)、方法区(Method Area)、程序计数器(PC ...

    JAVA中对GC的理解

    Java中的GC,全称Garbage Collection,是一种自动的内存管理机制,主要负责回收不再使用的对象所占用的内存空间,防止内存泄漏,确保程序能够高效、稳定地运行。GC是Java虚拟机(JVM)的一个重要组成部分,它与C++等...

    面试官:详细谈谈Java对象的4种引用方式

    Java 对象的 4 种引用方式 在 Java 中,对象的引用方式有四种:强引用、软引用、弱引用和幻象引用。这些引用方式的使用场景和特点是不同的,下面我们详细介绍每种引用方式的特点和使用场景。 强引用 强引用是最...

    面向GC的Java编程Java开发Java经验技巧共7页

    下面将详细介绍面向GC的Java编程以及相关的开发经验技巧。 1. **理解Java内存模型** - Java内存分为堆(Heap)、栈(Stack)、方法区(Method Area)、本地方法栈(Native Method Stack)和程序计数器(PC ...

Global site tag (gtag.js) - Google Analytics