当前位置:编程学习 > JAVA >>

java 引用对象分类

今天我们来讲讲java的引用对象的分类.讲这个之前需要先了解一下java的回收机制:

------------------------------------------------------------------------------------------------------------------


 Java2平台里面引入了java.lang.ref包,这个包中的类可以让我们引用对象,而是的这些对象不用停留在内存中。不仅仅如此,这些类和Java本身的垃圾回收器还存在一定的交互。我们平时开发的时候有时候会用到System.gc()方法,而Java里面GC的基本工作原理又是如何呢?当然使用Java引用类主要目的是为了和Java垃圾回收器进行有限的交互,从而全面提升内存的管理效率,这种情况在嵌入式系统和实时系统以及一些对内存要求严格的系统里面比较凑效。

1)GC的基本原理

  Java的内存管理主要指的是对内存中的对象的管理,包括针对这些对象进行内存的分配和释放等各种操作,学过Java的人都了解Java本身的内存模型,对于一个Java的对象而言,存储主要分为两种,一种是内存堆(Heap),内存堆是无序的,主要用来存放创建的Java对象;一种是内存栈(Stack),主要用来存放Java引用,然后在管理过程使用Java引用指向Java对象。而GC就是负责在对象“不可达”的时候将对象回收,常见的语句例如:

Object o = null;

  而CG本身是如何工作的呢?当系统在创建对象的时候,即当我们使用new关键字创建一个对象的时候,GC就开始监控对象的地址、大小以及使用状态。一般情况下,Java的GC机制都有特定的回收算法,GC通常会使用有向图的方式来记录队中的所有对象,通过此种方式确定哪些对象是“可达的”,而哪些是“不可达的”。当GC判断一些对象不可达的时候,GC就有责任回收相关内存空间。但是,因为平台的不同,往往在调用System.gc()的时候,存在太多不确定性,可以这样认为,当程序调用了System.gc()过后,仅仅是程序向JVM发送了请求,至于JVM究竟在什么时候进行垃圾回收,不同的平台不一样。(*:需要解决的误区是不要觉得System.gc()调用了过后,垃圾回收器一定会对系统内存进行回收,系统回收相关内存取决于平台和系统。)

2)增量方式GC(IncrementalGC)

  GC在JVM中通常是启动了一个新的进程或者一组新的进程,它本身和Java用户程序一样需要占用heap空间,运行时也占用CPU。设计GC的时候,必须要在停顿时间和回收率之间进行权衡,原因在于它本身占用了Heap,如果GC运行时间太长,用户就会感觉到Java程序本身会有一定的停顿,如果运行时间太短,则有很多内存没有回收,使得程序里面创建的Java对象占用了大量的内存。增量方式的GC就是通过一定的回收算法,把一个长时间的中断,分成很多小的中断,通过这种方式减少GC对程序本身的影响。其实增量方式整体性能比不上普通的高,但是能够减少停顿时间,改善使用者的用户体验。当然除了这样的方式,GC整体的方式为:

  引用计数法(ReferenceCounting Collector) ;

  Tracing算法(Tracing Collector);

  Compacting算法(Compacting Collector) ;

  Copying算法(Coping Collector) ;

  Generation算法(Generational Collector) ;

  Adaptive算法(Adaptive Collector) ;

  至于相关的算法可以去查阅相关的文档

======================================================================================================

下面开始讲java引用的分类:


 Java中的对象引用主要有以下几种类型:

  1)强可及对象(strongly reachable):

  可以通过强引用访问的对象,一般来说,我们平时写代码的方式都是使用的强引用对象,比如下边的代码段:

  StringBuilderbuilder= new StringBuilder();

  上边代码部分引用obj这个引用将引用内存堆中的一个对象,这种情况下,只要obj的引用存在,垃圾回收器就永远不会释放该对象的存储空间。这种对象我们又成为强引用(Strongreferences),这种强引用方式就是Java语言的原生的Java引用,我们几乎每天编程的时候都用到。上边代码JVM存储了一个StringBuilder类型的对象的强引用在变量builder呢。强引用和GC的交互是这样的,如果一个对象通过强引用可达或者通过强引用链可达的话这种对象就成为强可及对象,这种情况下的对象垃圾回收器不予理睬。如果我们开发过程不需要垃圾回器回收该对象,就直接将该对象赋为前引用。


  2)软可及对象(softlyreachable): www.zzzyk.com

  不通过强引用访问的对象,即不是强可及对象,但是可以通过软引用访问的对象就成为软可及对象,软可及对象就需要使用类SoftReference(java.lang.ref.SoftReference)。此种类型的引用主要用于内存比较敏感的高速缓存,而且此种引用还是具有较强的引用功能,当内存不够的时候GC会回收这类内存,因此如果内存充足的时候,这种引用通常不会被回收的。不仅仅如此,这种引用对象在JVM里面保证在抛出OutOfMemory异常之前,设置成为null。通俗地讲,这种类型的引用保证在JVM内存不足的时候全部被清楚,但是有个关键在于:垃圾收集器在运行时是否释放软可及对象是不确定的,而且使用垃圾回收算法并不能保证一次性寻找到所有的软可及对象。当垃圾回收器每次运行的时候都可以随意释放不是强可及对象占用的内存,如果垃圾回收器找到了软可及对象过后,可能会进行以下操作:

【1】将SoftReference对象的referent域设置成为null,从而使该对象不再引用heap对象。

【2】SoftReference引用过的内存堆上的对象一律被生命为finalizable。

【3】当内存堆上的对象finalize()方法被运行而且该对象占用的内存被释放,SoftReference对象就会被添加到它的ReferenceQueue,前提条件是ReferenceQueue本身是存在的。

  既然Java里面存在这样的对象,那么我们在编写代码的时候如何创建这样的对象呢?创建步骤如下:

  先创建一个对象,并使用普通引用方式【强引用】,然后再创建一个SoftReference来引用该对象,最后将普通引用设置为null,通过这样的方式,这个对象就仅仅保留了一个SoftReference引用,同时这种情况我们所创建的对象就是SoftReference对象。一般情况下,我们可以使用该引用来完成Cache功能,就是前边说的用于高速缓存,保证最大限度使用内存而不会引起内存泄漏的情况。下边的代码段:

  publicstatic void main(String args[])

  {

    //创建一个强可及对象

    A a = newA();

    //创建这个对象的软引用SoftReference

    SoftReferencesr = new SoftReference(a);

    //将强引用设置为空,以遍垃圾回收器回收强引用

    a = null;

    //下次使用该对象的操作

    if( sr !=null ){

      a= (A)sr.get();

    }else{

      //这种情况就是由于内存过低,已经将软引用释放了,因此需要重新装载一次

      a= new A();

      sr= new SoftReference(a);

    }

  }

  软引用技术使得Java系统可以更好地管理内存,保持系统稳定,防止内存泄漏,避免系统崩溃,因此在处理一些内存占用大而且生命周期长使用不频繁的对象可以使用该技术。

  3)弱可及对象(weakly reachable):

  不是强可及对象同样也不是软可及对象,仅仅通过弱引用WeakReference(java.lang.ref.WeakReference)访问的对象,这种对象的用途在于规范化映射(canonicalized mapping),对于生存周期相对比较长而且重新创建的时候开销少的对象,弱引用也比较有用,和软引用对象不同的是,垃圾回收器如果碰到了弱可及对象,将释放WeakReference对象的内存,但是垃圾回收器需要运行很多次才能够找到弱可及对象。弱引用对象在使用的时候,可以配合ReferenceQueue类使用,如果弱引用被回收,JVM就会把这个弱引用加入到相关的引用队列中去。最简单的弱引用方法如以下代码:

  WeakReferenceweakWidget = new WeakReference(classA);

  在上边代码里面,当我们使用weakWidget.get()来获取classA的时候,由于弱引用本身是无法阻止垃圾回收的,所以我们也许会拿到一个null为返回。【*:这里提供一个小技巧,如果我们希望取得某个对象的信息,但是又不影响该对象的垃圾回收过程,我们就可以使用WeakReference来记住该对象,一般我们在开发调试器和优化器的时候使用这个是很好的一个手段。】

  如果上边的代码部分,我们通过weakWidget.get()返回的是null就证明该对象已经被垃圾回收器回收了,而这种情况下弱引用对象就失去了使用价值,GC就会定义为需要进行清除工作。这种情况下弱引用无法引用任何对象,所以在JVM里面就成为了一个死引用,这就是为什么我们有时候需要通过ReferenceQueue类来配合使用的原因,使用了ReferenceQueue过后,就使得我们更加容易监视该引用的对象,如果我们通过一ReferenceQueue类来构造一个若引用,当若引用的对象已经被回收的时候,系统将自动使用对象引用队列来代替对象引用,而且我们可以通过ReferenceQueue类的运行来决定是否真正要从垃圾回收器里面将该死引用(DeadReference)清除。

  弱引用代码段:

  //创建普通引用对象

  MyObjectobject = new MyObject();

  //创建一个引用队列

  ReferenceQueuerq = new ReferenceQueue();

  //使用引用队列创建MyObject的弱引用

  WeakReferencewr = new WeakReference(object,rq);

  这里提供两个实在的场景来描述弱引用的相关用法:

  (1)你想给对象附加一些信息,于是你

补充:软件开发 , Java ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,