Android性能优化(一)--关于内存溢出

3/8/2017来源:ASP.NET技巧人气:3452

一个善于自嘲的人,不是因为内心强大,而只是想巧妙地让别人闭嘴。(心情标签)这里写图片描述

关于内存溢出的问题

关于栈内存溢出的问题,想必大家或多或少都会遇到过,不像好久不见,却如胶似漆,不经意间都会碰到<( ̄︶ ̄)>.我同桌曾经还对我那么一句煽情却又无比夸张的话,”我敲代码一天不遇到内存溢出,我就难受“,他也是够了….. 今天就以我目前所了解到各种关于内存溢出产生的原因和解决方法,分别与大家分享分享,内容不当或知识点理解片面错误,还望指出.

1.关于优化的了解

Android堆内存也可自己定义大小

对于一些Android项目,影响性能瓶颈的主要是Android自己内存管理机制问题,目前手机厂商对RAM都比较吝啬,对于软件的流畅性来说RAM对性能的影响十分敏感,除了 优化Dalvik虚拟机的堆内存分配外, 我们还可以强制定义自己软件的对内存大小,我们使用Dalvik提供的dalvik.system.VMRuntime类来设置最小堆内存为例:

PRivate final static int CWJ_HEAP_SIZE = 6* 1024* 1024 ; VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); //设置最小heap内存为6MB大小。当然对于内存吃紧来说还可以通过手动干涉GC去处理

2.从一个加载图片时产生内存溢出产生的问题开始分析

图片过大导致OOM Android中用bitmap时很容易内存溢出,比如报如下错误:java.lang.OutOfMemoryError :bitmap size exceeds VM budget。

Picasso.with(context).load(rowItem.getProductImages().get(0)).into(holder.productImageView); 错误: 2771-2793/com.koove E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 31961100 byte allocation with 4194304 free bytes and 27MB until OOM" 03-25 09:53:23.666 2771-2793/com.koove D/skia﹕ --- decoder->decode returned false

1.回到原点:想通过这些代码实现什么功能? 加载一些图片是照片能正常显示到模拟器中

2.产生的原因? 因为 ImeView 在底层创建图片层的时候 会占用很大的内存空间,Android加载大量图片的时候就会造成内存溢出

3.分析: 前面说到,imageView 在底层创建图片层的时候 会占用很大的内存空间,所以我们可以

尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的source.

decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间

4. 解决或避免的方法: ♥解决方法一: 在从网络或本地加载图片的时候,只加载缩略图。 这个方法的确能够少占用不少内存,优点是图像在尽可能低的分辨率的情况下, 较低的分辨率意味着更少的数据保存在缓存中。可是它的致命的缺点就是,因为加载的是缩略图,所以图片失真就比较严重咯,

♥解决方法二: 读取图片时注意方法的调用,适当压缩 这里写图片描述

以上代码可以优化内存溢出,但它只是改变图片大小,并不能彻底解决内存溢出。

♥解决方法三: 运用JAVA的软引用,及时地recyle()操作 ,进行图片缓存,将经常需要加载的图片,存放在缓存里,避免反复加载。 这里写图片描述

♥解决方法四: 及时销毁不再使用的Bitmap对象。


3.归纳内存溢出缘由

一)是否App中的类中和引用变量过多使用了Static修饰 如public staitc Student s;在类中的属性中使用 static修饰的最好只用基本类型或字符串。如public static int i = 0; //public static String str;

二)是否App中使用了大量的递归或无限递归(递归中用到了大量的建新的对象)

三)是否App中使用了大量循环或死循环(循环中用到了大量的新建的对象)

四)检查App中是否使用了向数据库查询所有记录的方法。即一次性全部查询的方法,如果数据量超过10万多条了,就可能会造成内存溢出。所以在查询时应采用“分页查询”。

五)检查是否有数组,List,Map中存放的是对象的引用而不是对象,因为这些引用会让对应的对象不能被释放。会大量存储在内存中。

六)检查是否使用了“非字面量字符串进行+”的操作。因为String类的内容是不可变的,每次运行”+”就会产生新的对象,如果过多会造成新String对象过多,从而导致JVM没有及时回收而出现内存溢出。

4. 什么情况下回导致内存溢出

内存溢出的几点原因: 1. 资源释放问题 程序代码的问题,长期保持某些资源,如Context、Cursor、IO流的引用,资源得不到释放 造成内存泄露。 2. 对象内存过大问题 保存了多个耗用内存过大的对象(如Bitmap、xml文件),造成内存超出限制。 3.static 关键字的使用问题 static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是 该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费 过多的实例(Context的情况最多),这时就要谨慎对待了。 这里写图片描述 以上的代码是很危险的,如果将 Activity 赋值到 mContext 的话。那么即使该 Activity 已经 onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

针对static的解决方案

1) 应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。 2) Context尽量使用applicationContext,因为Application的Context的生命周期比较 长,引用它不会出现内存泄露的问题。 3) 使用 WeakReference 代替强引用。比如可以使用 WeakReference mContextRef; 4. 线程导致内存溢出 线程产生内存泄露的主要原因在于线程生命周期的不可控。

针对这种线程导致的内存泄露问题的解决方案: (一) 将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静 态类则不拥有)。 (二) 在线程内部采用弱引用保存Context引用。

5.利用工具进行辅助排查

(一)TraceView简介 Traceview是 Android平台特有的数据采集和分析工具,它主要用于分析 Android中应用程 序的hotspot(瓶颈)。Traceview本身只是一个数据分析工具,而数据的采集则需要使用Android SDK中的Debug类或者利用DDMS工具。

(二)heap简介 heap工具可以帮助我们检查代码中是否存在会造成内存泄漏的地方。 用heap监测应用进程使用内存情况的步骤如下: 1.启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的; 2.点击选中想要监测的进程,比如system_process进程; 3.点击选中Devices视图界面中最上方一排图标中的“Update Heap”图标; 4.点击Heap视图中的“Cause GC”按钮; 5.此时在Heap视图中就会看到当前选中的进程的内存使用量的详细情况。 说明: a. 点击“Cause GC”按钮相当于向虚拟机请求了一次gc操作; b. 当内存使用信息第一次显示以后,无须再不断的点击“CauseGC”,Heap视图界面会定时 刷新,在对应用的不断的操作过程中就可以看到内存使用的变化; c. 内存使用信息的各项参数根据名称即可知道其意思,在此不再赘述。

(三)allocation tracker 简介

allocation tracker是内存分配跟踪工具 步骤: 运行DDMS,只需简单的选择应用进程并单击Allocation tracker 标签,就会打开一个新的窗口, 单击“Start Tracing”按钮; 然后,让应用运行你想分析的代码。运行完毕后,单击“Get Allocations”按钮,一个已分配对象 的列表就会出现第一个表格中。 单击第一个表格中的任何一项,在表格二中就会出现导致该内存分配的栈跟踪信息。通过allocation tracker,不仅知道分配了哪类对象,还可以知道在哪个线程、哪个类、哪个文件的哪一行。

6. 如何避免OOM异常

1、图片过大导致OOM 解决方法: 方法1: 等比例缩小图片

方法2:对图片采用软引用,及时地进行recyle()操作

方法3:使用加载图片框架处理图片,如专业处理加载图片的ImageLoader图片加载框架。还有XUtils的BitMapUtils来做处理

2、界面切换导致OOM 一般情况下,开发中都会禁止横屏的。因为如果是来回切换话,activity 的生命周期会重新销毁 然后创建。 有时候我们会发现这样的问题,横竖屏切换N次后 OOM了。

解决办法: 1、看看页面布局当中有没有大的图片,比如背景图之类的。 去除xml中相关设置,改在程序中设置背景图(放在onCreate()方法中):

Drawable drawable = getResources().getDrawable(R.drawable.id); ImageView imageView = new ImageView(this); imageView.setBackgroundDrawable(drawable);

在Activity destory 时注意,drawable.setCallback(null); 防止Activity得不到及时的释放

2.或者可以直接把 xml 配置文件加载成 view 再放到一个容器里,然后直接调用 this.setContentView(View view);方法,避免xml的重复加载。

3、 在页面切换时尽可能少地重复使用一些代码 比如:重复调用数据库,反复使用某些对象等等……

3.查询数据库中有没有关闭游标 程序中经常会进行查询数据库的操作,如利用一个号码归属地查询的小demo , 但是经常会有使用完毕Cursor后没有关闭的情况。如果 我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会出现内 存问题,这样就会给以后的测试和问题排查带来困难和风险。

4、构造Adapter时,没有使用缓存的 convertView 在使用ListView的时候通常会使用Adapter,那么我们应该尽可能的使用ConvertView。 为什么要使用convertView? 当convertView为空时,用setTag()方法为每个View绑定一个存放控件的ViewHolder对象。 当 convertView 不为空,重复利用已经创建的 view 的时候,使用 getTag()方法获取绑定的 ViewHolder对象,这样就避免了findViewById对控件的层层查询,而是快速定位到控件。 5、Bitmap对象不再使用时调用recycle()释放内存 有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不再被使用的时 候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是必须的,视情况而定。 6、其他 Android 应用程序中最典型的需要注意释放资源的情况是在 Activity 的生命周期中,在onPause()、onStop()、 onDestroy()方法中需要适当的释放资源的情况。使用广播没有注销也会产生OOM。


嗯.关于内存溢出的问题肯定不止于这些,今天就写到这,之后遇到再进行更新改进吧.针对肯能出现的错误还请大家指出,晚安.<( ̄ˇ ̄)/