资讯专栏INFORMATION COLUMN

java.lang.OutOfMemoryError异常解决方法

Lsnsh / 1310人阅读

摘要:另外再写了一个的抽象方法,该方法在里面调用。去除中相关设置,改在程序中设置背景图放在方法中尽量不使用静态的图片和全局性的图片在时注意,防止得不到及时的释放。等等,还需要进一步地研究

引言

java.lang.OutOfMemoryError简称OOM内存溢出,这是一种很常见的导致的程序崩溃的问题,但也是很容易被开发者忽视的一个问题,因为它不像java.lang.NullPointerException这样的错误,程序一运行就能被发现,它不是每次运行或每台手机都出现,有时可能要等到项目上线,后台产生了大量数据之后才能被发现。
最近做了一个新闻类的项目,和商城类的项目相比,由于涉及更多的图片和视频,很明显数据量要大得多,所以更容易产生OOM的问题。如何解决这个异常呢,首先我们来看看它是怎么产生的。

产生原因

常见原因有以下几种:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小;

对应的Log显示的错误提示如下:
1.tomcat:java.lang.OutOfMemoryError: PermGen space
2.tomcat:java.lang.OutOfMemoryError: Java heap space
3.weblogic:Root cause of ServletException java.lang.OutOfMemoryError
4.resin:java.lang.OutOfMemoryError
5.java:java.lang.OutOfMemoryError

解决方案

1.避免循环产生过多重复的对象实体

个人觉得对于这个问题开发程序时应该是可以避免的,即使写程序时没有考虑到,发现是这个原因造成的,应该也是很容易解决的;
例如,对复杂的listview进行合理设计与编码,注意重用Adapter里面的convertView,以及holder机制的运用

2.自定义内存大小和优化Dalvik虚拟机的堆内存

在Android2.2之前,有这样一个类dalvik.system.VMRuntime,它提供的setTargetHeapUtilization()方法可以增强程序堆内存的处理效率,代码如下:

//在程序onCreate时就可以调用即可

private final static floatTARGET_HEAP_UTILIZATION = 0.75f;  
VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 

还有setMinimumHeapSize()方法可以自定义应用需要多大的内存,强行设置最小内存大小,
//设置最小heap内存为6MB大小

private final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; 
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); 

但是上面说了这种方法只适用于Android2.2以下,否则编译是通不过的,既然取消了这个方法,肯定有能够代替这个方法,且比这个方法更完美的解决方案,这就是下面我要重点说的几种方法;

3.Fragment的懒加载

现在大多数程序一个Activity里面可能会以viewpager(或其他容器)与多个Fragment来组合使用,而如果每个fragment都需要去加载数据,或从本地加载,或从网络加载,那么在这个activity刚创建的时候就变成需要初始化大量资源。那么,能不能做到当切换到这个fragment的时候,它才去初始化呢?

答案就在Fragment里的setUserVisibleHint这个方法里,该方法用于告诉系统,这个Fragment的UI是否是可见的。所以我们只需要继承Fragment并重写该方法,即可实现在fragment可见时才进行数据加载操作,即Fragment的懒加载。

代码如下:

    public abstract class LazyFragment extends Fragment {  
        protected boolean isVisible;
    @Override  
    public void setUserVisibleHint(boolean isVisibleToUser) {  
        super.setUserVisibleHint(isVisibleToUser);  
        if(getUserVisibleHint()) {  
            isVisible = true;  
            onVisible();  
        } else {  
            isVisible = false;  
            onInvisible();  
        }  
    }  
  
    protected void onVisible(){  
        lazyLoad();  
    }  
  
    protected abstract void lazyLoad();  
  
    protected void onInvisible(){}  
}

在LazyFragment,我增加了三个方法,一个是onVisiable,即fragment被设置为可见时调用,一个是onInvisible,即fragment被设置为不可见时调用。另外再写了一个lazyLoad的抽象方法,该方法在onVisible里面调用。

你可能会想,为什么不在getUserVisibleHint里面就直接调用呢?我这么写是为了代码的复用。因为在fragment中,我们还需要创建视图(onCreateView()方法),可能还需要在它不可见时就进行其他小量的初始化操作(比如初始化需要通过AIDL调用的远程服务)等。

而setUserVisibleHint是在onCreateView之前调用的,那么在视图未初始化的时候,在lazyLoad当中就使用的话,就会有空指针的异常。而把lazyLoad抽离成一个方法,那么它的子类就可以这样做:

public class OpenResultFragment extends LazyFragment{  
    // 标志位,标志已经初始化完成。  
    private boolean isPrepared;  
  
    @Override  
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
        Log.d(LOG_TAG, "onCreateView");  
        View view = inflater.inflate(R.layout.fragment_open_result, container, false);  
        //XXX初始化view的各控件  
    isPrepared = true;  
        lazyLoad();  
        return view;  
    }  
  
    @Override  
    protected void lazyLoad() {  
        if(!isPrepared || !isVisible) {  
            return;  
        }  
        //填充各控件的数据  
    }  
  
}  

在上面的类当中,我们增加了一个标志位isPrepared,用于标志是否初始化完成。然后在我们所需要的初始化操作完成之后调用,如上面的例子当中,在初始化view之后,设置 isPrepared为true,同时调用lazyLoad()方法。而在lazyLoad()当中,判断isPrepared和isVisible只要有一个不为true就不往下执行。也就是仅当初始化完成,并且可见的时候才继续加载,这样的避免了未初始化完成就使用而带来的问题。

4.利用BitmapFactory.Options限制图片的大小

有时加载数据量并不是很大时也会产生OOM异常,一般是由于加载图片造成的,Bitmap加载图片最终都是通过java层的createBitmap来完成的,需要消耗大量内存,我们可以利用BitmapFactory.Options限制图片的大小,降低图片质量,减少图片所占内存

InputStream is = this.getResources().openRawResource(R.drawable.pic1); 
BitmapFactory.Options options=new BitmapFactory.Options(); 
options.inJustDecodeBounds = false; 
options.inSampleSize = 10;   //width,hight设为原来的十分一 
Bitmap btp =BitmapFactory.decodeStream(is,null,options); 
 

5.及时对图片进行recyle()操作

if(!bmp.isRecycle() ){ 
         bmp.recycle()   //回收图片所占的内存 
         system.gc()  //提醒系统及时回收 
} 
总结

以上是OOM的常见的几种产生原因和解决方案,还有很多方法,例如,
还有看看页面布局当中有没有大的图片,比如背景图之类的。去除xml中相关设置,改在程序中设置背景图(放在onCreate()方法中);
尽量不使用静态的图片和全局性的图片;
在Activity destory时注意,bg.setCallback(null); 防止Activity得不到及时的释放。
等等,还需要进一步地研究

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12728.html

相关文章

  • Java 常见内存溢出异常与代码实现

    摘要:堆堆是用来存储对象实例的因此如果我们不断地创建对象并且保证和创建的对象之间有可达路径以免对象被垃圾回收那么当创建的对象过多时会导致内存不足进而引发异常上面是一个引发异常的代码我们可以看到它就是通过不断地创建对象并将对象保存在中防止其被 Java 堆 OutOfMemoryError Java 堆是用来存储对象实例的, 因此如果我们不断地创建对象, 并且保证 GC Root 和创建的对象...

    whatsns 评论0 收藏0
  • 十种JVM内存溢出的情况,你碰到过几种?

    摘要:内存溢出的情况就是从类加载器加载的时候开始出现的,内存溢出分为两大类和。以下举出个内存溢出的情况,并通过实例代码的方式讲解了是如何出现内存溢出的。内存溢出问题描述元空间的溢出,系统会抛出。这样就会造成栈的内存溢出。 导言: 对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不需要自己实现释放内存,不容易出现内存泄漏和内存溢出的问题,由虚拟机管理内存这一切看起来非常美好,但是一旦...

    ShevaKuilin 评论0 收藏0
  • 【修炼内功】[JVM] 浅谈虚拟机内存模型

    摘要:也正是因此,一旦出现内存泄漏或溢出问题,如果不了解的内存管理原理,那么将会对问题的排查带来极大的困难。 本文已收录【修炼内功】跃迁之路 showImg(https://segmentfault.com/img/bVbsP9I?w=1024&h=580); 不论做技术还是做业务,对于Java开发人员来讲,理解JVM各种原理的重要性不必再多言 对于C/C++而言,可以轻易地操作任意地址的...

    sanyang 评论0 收藏0
  • 系统优化怎么做-Tomcat优化

    摘要:运行模式分种模式一般使用模式效率低对系统配置有一些比较高的要求确认的运行模式配置文件关键配置最大线程数默认是最小活跃线程数默认是最大的等待队列个数,超过则请求拒绝默认值是,一般不改变。 前言 Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的,应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能。另外必须提到对应的JVM参数的优化...

    dkzwm 评论0 收藏0
  • 系统优化怎么做-Tomcat优化

    摘要:运行模式分种模式一般使用模式效率低对系统配置有一些比较高的要求确认的运行模式配置文件关键配置最大线程数默认是最小活跃线程数默认是最大的等待队列个数,超过则请求拒绝默认值是,一般不改变。 前言 Tomcat作为Web应用的服务器,目前绝大多数公司都是用其作为应用服务器的,应用服务器的执行效率会影响系统执行,这里会讲Tomcat怎样进行配置能提高处理性能。另外必须提到对应的JVM参数的优化...

    gghyoo 评论0 收藏0

发表评论

0条评论

Lsnsh

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<