当前位置: 首页 > 科技观察

Java如何有效避免OOM:善于使用软引用和弱引用

时间:2023-03-12 06:23:07 科技观察

想必很多朋友都对OOM(OutOfMemory)的错误不陌生,但是遇到这个错误又该如何有效解决呢?今天我们就来说说如何利用软引用和弱引用来有效解决程序中的OOM问题。以下是本文的目录大纲:1.理解强引用、软引用、弱引用、幻引用的概念2.进一步理解软引用和弱引用3.如何使用软引用和弱引用解决OOM问题如有不妥之处还望理解批评指正,万分感谢。1.理解强引用、软引用、弱引用、幻引用的概念在Java中,虽然不需要程序员手动管理对象的生命周期,但是如果希望某些对象具有一定的生命周期(例如,当内存不足时JVM会自动回收一些对象来避免OutOfMemory错误)你需要使用软引用和弱引用。从JavaSE2开始,提供了四种类型的引用:强引用、软引用、弱引用和虚引用。Java中提供这四种引用类型的目的主要有两个:第一个是让程序员通过代码来确定某些对象的生命周期;第二个是为了便于JVM进行垃圾收集。下面分别解释一下这四种引用的概念:1、强引用(StrongReference)强引用指的是程序代码中无处不在。比如下面代码中的object和str就是强引用:Objectobject=newObject();Stringstr="hello";只要一个对象关联了强引用,JVM就绝对不会回收这个对象。即使在内存不足的情况下,JVM也宁愿抛出OutOfMemory错误也不愿回收这个对象。例如,下面的代码:publicclassMain{publicstaticvoidmain(String[]args){newMain().fun1();}publicvoidfun1(){Objectobject=newObject();Object[]objArr=newObject[1000];}}如果你想要中断强引用与对象的关联,可以显式地将引用赋值给null,这样JVM就会在合适的时候回收对象。  运行到Object[]objArr=newObject[1000];这句时,如果内存不足,JVM会抛出OOM错误,不会回收object指向的对象。但是需要注意的是,fun1运行后,object和objArr已经不存在了,所以它们指向的对象会被JVM回收。例如,在Vector类的clear方法中,清理是通过将引用分配给null来实现的:*@paramindextheindexoftheelementtoberemoved*@returnelementthatwasremoved*@since1.2*/publicsynchronizedEremove(intindex){modCount++;if(index>=elementCount)thrownewArrayIndexOutOfBoundsException(index);ObjectoldValue-num-elementData[index]=intindex-1;if(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);elementData[--elementCount]=null;//让gcdoitsworkreturn(E)oldValue;}2.软引用(SoftReference)软引用用来描述一些有用但不是必须的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联的对象,JVM只有在内存不足时才会回收对象。所以这一点可以用来解决OOM的问题,这个特性非常适合做缓存:比如网页缓存,图片缓存等。软引用可以与引用队列(ReferenceQueue)结合使用。如果软引用所引用的对象被JVM回收,则软引用会被添加到与其关联的引用队列中。这是一个示例用法:importjava.lang.ref.SoftReference;publicclassMain{publicstaticvoidmain(String[]args){SoftReferencesr=newSoftReference(newString("hello"));System.out.println(sr.get());}}3.弱引用(WeakReference)弱引用也用于描述非必需的对象。JVM在进行垃圾回收时,无论内存是否充足,弱引用关联的对象都会被回收。在java中,它由java.lang.ref.WeakReference类表示。以下是使用示例:importjava.lang.ref.WeakReference;publicclassMain{publicstaticvoidmain(String[]args){WeakReferencesr=newWeakReference(newString("hello"));System.out.println(sr.get());System.gc();//通知JVM的gc进行垃圾回收当JVM执行垃圾收集时,必须回收与弱引用关联的对象。不过需要注意的是,这里所说的弱引用关联对象,是指只有弱引用关联到它。如果同时有强引用与之关联,则在垃圾回收时不会回收该对象(软引用也是如此。)。弱引用可以与引用队列(ReferenceQueue)结合使用。如果弱引用所引用的对象被JVM回收,那么软引用就会被添加到与其关联的引用队列中。4.幻引用(PhantomReference)幻引用不同于以往的软引用和弱引用,它不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与一个虚引用相关联,那么它很可能随时被垃圾收集器回收,就好像没有任何引用与之相关联一样。需要注意的是,虚引用必须与引用队列结合使用。当垃圾回收器要回收一个对象时,如果发现它还有一个虚引用,就会把这个虚引用添加到与之关联的引用队列中。程序可以通过判断引用队列中是否加入了幻引用来获知被引用对象是否会被垃圾回收。如果程序发现引用队列中加入了虚引用,则可以在被引用对象的内存被回收之前采取必要的动作。2.进一步理解软引用和弱引用String>pr=newPhantomReference(newString("hello"),queue);System.out.println(pr.get());}}对于强引用,我们在写代码的时候经常会用到。对于其他三种类型的引用,软引用和弱引用是最常用的。这两种类型既有相同点也有不同点。它们都是用来描述非必需对象的,但是软引用关联的对象只会在内存不足时才会被回收,而弱引用关联的对象在JVM进行垃圾回收时总会被回收。SoftReference类中,一共有三个方法,两个构造方法和一个get方法(类似于WekReference):两个构造方法:publicSoftReference(Treferent){super(referent);this.timestamp=clock;}publicSoftReference(Treferent,ReferenceQueueq){super(referent,q);this.timestamp=clock;}在使用软引用和弱引用的时候,我们可以通过System.gc()显式通知JVM进行垃圾回收,但是要注意,虽然发出了通知,但JVM不一定会立即执行,也就是说这句话不能保证JVM此时会进行垃圾回收。  get方法用于获取软引用关联对象的引用,如果对象被回收则返回null。3、如何使用软引用和弱引用解决OOM问题。上面讲了软引用和弱引用的基础知识,那么如何利用它们来优化程序性能,避免OOM问题呢?举个例子,如果一个应用程序需要读取大量的本地图片,如果每次都从硬盘读取图片,会严重影响性能,但是如果全部加载到内存中,可能会导致内存不足溢出。这时候使用软引用就可以解决这个问题。设计思路是:用一个HashMap来保存图片的路径和对应图片对象关联的软引用的映射关系。当内存不足时,JVM会自动回收这些缓存的图片对象占用的空间,从而有效避免了OOM问题。在Android开发中,经常用于下载大量图片。以下代码摘自博客:http://blog.csdn.net/arui319/article/details/8489451.....privateMap>imageCache=newHashMap>();
....publicvoidaddBitmapToCache(Stringpath){//强引用Bitmap对象Bitmapbitmap=BitmapFactory.decodeFile(path);//软引用Bitmap对象SoftReferencesoftBitmap=newSoftReference(bitmap);//将这个对象添加到Map中,使其缓存imageCache.put(path,softBitmap);}publicBitmapgetBitmapByPath(Stringpath){//从缓存中获取软引用的Bitmap对象SoftReferencesoftBitmap=imageCache.get(path);//判断是否存在软引用if(softBitmap==null){returnnull;}//取出Bitmap对象,如果Bitmap因内存不足被回收,则得到一个空的Bitmapbitmap=softBitmap.get();返回位图;}