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.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。



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

时间: 2024-12-15 01:50:13

Android性能优化(一)--关于内存溢出的相关文章

Android性能优化之避免内存泄漏的建议

在android程序开发中,内存泄漏问题是比较常见的问题,相信有过一些android编程经历的程序猿都遇到过各种各样的内存泄漏.内存泄漏是造成应用程序OOM的主要原因之一,是编程中必须避免的问题.下面小编搜罗了几个避免内存泄漏的建议,分享给大家,一起来看看吧. 1.对于生命周期比Activity长的对象,如果需要应该使用ApplicationContext : 2.在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Co

Android性能优化系列之apk瘦身

Android性能优化系列之布局优化 Android性能优化系列之内存优化 为什么APK要瘦身.APK越大,在下载安装过程中,他们耗费的流量会越多,安装等待时间也会越长:对于产品本身,意味着下载转化率会越低(因为竞品中,用户有更多机会选择那个体验最好,功能最多,性能最好,包最小的),所以apk的瘦身优化也很重要,本篇博客将讲述apk瘦身的相关内容. 包体分析 在Android Studio工具栏里,打开build–>Analyze APK, 选择要分析的APK包 可以看到占用空间的主要是代码.图

Android性能优化系列之App启动优化

Android性能优化系列之布局优化 Android性能优化系列之内存优化 Android性能优化系列之apk瘦身 应用的启动速度缓慢是我们在开发过程中常常会遇到的问题,比方启动缓慢导致的黑屏.白屏问题,本篇博客就将介绍App启动优化的相关知识. 应用的启动方式 通常来说,启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时.后台没有该应用的进程.这时系统会又一次创建一个新的进程分配给该应用.这个启动方式就是冷启动. 冷启动由于系统会又一次创建一个新的进程分配给它.所以会先创建和初始化A

[Android进阶]Android性能优化

Android性能优化 合理管理内存 节制的使用Service 如果应用程序需要使用Service来执行后台任务的话,只有当任务正在执行的时候才应该让Service运行起来.当启动一个Service时,系统会倾向于将这个Service所依赖的进程进行保留,系统可以在LRUcache当中缓存的进程数量也会减少,导致切换程序的时候耗费更多性能.我们可以使用IntentService,当后台任务执行结束后会自动停止,避免了Service的内存泄漏. 当界面不可见时释放内存 当用户打开了另外一个程序,我

Android 性能优化之内存泄漏检测以及内存优化(上)

在 Java 中,内存的分配是由程序完成的,而内存的释放则是由 Garbage Collecation(GC) 完成的,Java/Android 程序员不用像 C/C++ 程序员一样手动调用相关函数来管理内存的分配和释放,虽然方便了很多,但是这也就造成了内存泄漏的可能性,所以记录一下针对 Android 应用的内存泄漏的检测,处理和优化的相关内容,上篇主要会分析 Java/Android 的内存分配以及 GC 的详细分析,中篇会阐述 Android 内存泄漏的检测和内存泄漏的常见产生情景,下篇会

Android性能优化:手把手带你全面了解 内存泄露 &amp; 解决方案

. 简介 即 ML (Memory Leak)指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象2. 对应用程序的影响 容易使得应用程序发生内存溢出,即 OOM 内存溢出 简介: 示意图3. 发生内存泄露的本质原因 具体描述示意图 特别注意 从机制上的角度来说,由于 Java存在垃圾回收机制(GC),理应不存在内存泄露:出现内存泄露的原因仅仅是外部人为原因 = 无意识地持有对象引用,使得 持有引用者的生命周期 > 被引用者的生命周期4. 储备知识:Androi

[Android 性能优化系列]内存之终极篇--降低你的内存消耗

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/articles/memory.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 建议大家在看本文之前先去我的博客看看 [Android 性能优化系列]内存之基础篇--Andr

[Android 性能优化系列]内存之基础篇--Android怎样管理内存

大家假设喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处(http://blog.csdn.net/kifile),再次感谢 原文地址:http://developer.android.com/training/articles/memory.html 在接下来的一段时间里,我会每天翻译一部分关于性能提升的Android官方文档给大家 以下是本次的正文: ################ 随机訪问存储器(Ram) 无论在哪种软件

Android性能优化方法(七)

Java从JDK1.2版本开始,就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期.这四种级别由高到低依次为:强引用.软引用.弱引用和虚引用. 这里重点介绍一下软引用和弱引用. 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它:如果内存空间不足了,就会回收这些对象的内存.只要垃圾回收器没有回收它,该对象就可以被程序使用.软引用可用来实现内存敏感的高速缓存.软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,J