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

知识就是力量,时间就是生命。这篇文章主要讲述Android性能优化--关于内存溢出相关的知识,希望能为你提供帮助。
一个善于自嘲的人, 不是因为内心强大, 而只是想巧妙地让别人闭嘴。(心情标签)

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

文章图片

【Android性能优化--关于内存溢出】关于内存溢出的问题
关于栈内存溢出的问题,想必大家或多或少都会遇到过,不像好久不见,却如胶似漆,不经意间都会碰到< ( ̄ ︶ ̄ )> .我同桌曾经还对我那么一句煽情却又无比夸张的话,”我敲代码一天不遇到内存溢出,我就难受“,他也是够了…..
今天就以我目前所了解到各种关于内存溢出产生的原因和解决方法,分别与大家分享分享,内容不当或知识点理解片面错误,还望指出.
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.6662771-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. 解决或避免的方法:
?解决方法一:
在从网络或本地加载图片的时候, 只加载缩略图。
这个方法的确能够少占用不少内存, 优点是图像在尽可能低的分辨率的情况下, 较低的分辨率意味着更少的数据保存在缓存中。可是它的致命的缺点就是, 因为加载的是缩略图, 所以图片失真就比较严重咯,
?解决方法二:
读取图片时注意方法的调用, 适当压缩
Android性能优化--关于内存溢出

文章图片

以上代码可以优化内存溢出, 但它只是改变图片大小, 并不能彻底解决内存溢出。
?解决方法三:
运用JAVA的软引用, 及时地recyle()操作 ,进行图片缓存, 将经常需要加载的图片, 存放在缓存里, 避免反复加载。
Android性能优化--关于内存溢出

文章图片

?解决方法四:
及时销毁不再使用的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的情况最多) , 这时就要谨慎对待了。
Android性能优化--关于内存溢出

文章图片

以上的代码是很危险的, 如果将 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。
嗯.关于内存溢出的问题肯定不止于这些,今天就写到这,之后遇到再进行更新改进吧.针对肯能出现的错误还请大家指出,晚安.< ( ̄ ˇ ̄ )/

    推荐阅读