关于安卓性能和内存

内存相关的问题在面试中被问到的概率还是比较大的,而且内存优化对于一个程序的性能而言也是至关重要的,现在就让我们一起来学习吧!

不废话,直接上干货~

一、内存泄漏

内存泄漏就是我们对某一内存空间的使用完成后没有释放。

主要原因:导致内存泄漏最主要的原因就是某些长存对象持有了一些其它应该被回收的对象的引用,导致垃圾回收器无法去回收掉这些对象。

出现的场景:

1.数据库的cursor没有关闭;

2.构造adapter时,没有使用缓存contentview;

3.Bitmap对象不使用时采用recycle()释放内存;

4.activity中的对象的生命周期大于activity;

二、内存溢出

内存溢出通俗理解就是软件(应用)运行需要的内存,超出了它可用的最大内存。

出现的场景:

1、使用大的Bitmap图片;

三、内存优化方法:

1、应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。使用WeakReference代替强引用;

2、线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。将线程内部类,改为静态内部类;

3、Bitmap问题:可以说出现OutOfMemory问题的绝大多原因都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了;

如何解决Bitmap带给我们的内存问题?

1)及时的销毁,虽然系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。

2)设置一定的采样率,有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码: 

[java] view
plain
 copy

  1. BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一

3)使用LruCache缓存。

4、巧妙的运用软引用(SoftRefrence),有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放;

5、尽量使用9path图片,Adapter要使用convertView复用等等;

6、比较耗资源的对象及时的关闭,例如Cursor,各种传感器,Service 等等;

7、使用IntentService代替service,这种Service的最大特点就是当后台任务执行结束后会自动停止,在极大程度上避免了Service内存泄漏的可能性;

8、使用ProGuard,它除了混淆代码之外,还具有压缩和优化代码的功能。ProGuard会对我们的代码进行检索,删除一些无用的代码,并且会对类、字段、方法等进行重名,重命名之后的类、字段和方法名都会比原来简短很多,这样的话也就对内存的占用变得更少了;

ListView的优化方案:

1>、复用contentView:就是自定义适配器在getView方法中要考虑方法传进来的参数contentView是否为null,如果为null就创建contentView并返回,如果不为null则直接使用;在这个方法中尽可能少创建view;

2>、异步加载图片:给contentView设置tag(setTag()),传入一个viewHolder对象,下次可以直接调用getTag()显示缓存中的数据,可以达到图像数据异步加载的效果;

3>.快速滑动列表时不显示图片:

当快速滑动列表时(SCROLL_STATE_FLING),item中的图片获取需要消耗资源的view,可以不显示出来;而处于其他两种状态:空闲(SCROLL_STATE_IDLE)和低俗拖动(SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来。

如果 listview 需要显示的 item 很多,就要考虑分页加载。比如一共要显示100条或者更多的时候,我们可以考虑先加载20条,等用户拉到列表底部的时候,再去加载接下来的20 条。

4>.如果自定义的item中有图片,需要处理图片(减少图片所占内存);

1.对图片进行边界压缩 2.用option类来保存图片大小 3.避免图片的实时缩放,最好预先缩放到视图大小

5>.尽量避免在listview适配器中使用线程,线程是产生内存泄露的主要原因,因为线程的生命周期不可控;

我们用具体的代码来看看,怎么避免OOM

事例一:

[java] view
plain
 copy

  1. Handler mHandler = new Handler() {
  2. @Override
  3. public void handleMessage(Message msg) {
  4. mImageView.setImageBitmap(mBitmap);
  5. }
  6. }

在一个Activity中有一个Handler的内部类,Handler对象会隐式地持有该Activity的引用,而Handler通常会伴随着一个耗时的后台线程(例如网络下载图片)一起出现,这个后台线程在任务执行完毕之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束。

要解决这个问题有两种办法:

方法一:通过程序逻辑来进行保护。

1).在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。

2).如果你的Handler是被delay的Message持有了引用(调用了postDelayed,那么使用Handler的removeCallbacks()或removeCallbacksAndMessages(null)方法把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类,并且加上Activity的弱引用。

静态类不持有外部类的对象,所以你的Activity就可以被回收了。代码如下:

[java] view
plain
 copy

  1. static class MyHandler extends Handler {
  2. WeakReference<Activity > mActivityReference;
  3. MyHandler(Activity activity) {
  4. mActivityReference= new WeakReference<Activity>(activity);
  5. }
  6. @Override
  7. public void handleMessage(Message msg) {
  8. final Activity activity = mActivityReference.get();
  9. ImageView imageView= (ImageView)activity.findViewById(R.id.imageview);
  10. if (activity != null) {
  11. mImageView.setImageBitmap(mBitmap);
  12. }
  13. }
  14. }

事例二、

[java] view
plain
 copy

  1. new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. SystemClock.sleep(10000);
  5. }
  6. }).start();

在Activity里声明了一个匿名内部类,如果Activity在销毁之前,线程的任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。

解决办法就是使用静态内部类,并且及时关闭线程,如下:

[java] view
plain
 copy

  1. private static class MyThread extends Thread {
  2. private boolean mRunning = false;
  3. @Override
  4. public void run() {
  5. mRunning = true;
  6. while (mRunning) {
  7. SystemClock.sleep(1000);
  8. }
  9. }
  10. public void close() {
  11. mRunning = false;
  12. }
  13. }

我们在Activity退出时,可以在onDestroy()方法中显示的调用mThread.close();以此来结束该线程,这样在避免Activity OOM的同时也避免了线程的内存泄漏问题。

每一个非静态内部类实例都会持有一个外部类的引用,若该引用是Activity的引用,那么该Activity在被销毁时将无法被回收。如果你的静态内部类需要一个相关Activity的引用以确保功能能够正常运行,那么你得确保你在对象中使用的是一个Activity的弱引用。

这样看来static好像是解决OOM问题的重要武器,那是不是每个内部类都使用static来修改就好了呢,其实不然,只有当此类在全局多处用到时才这样做,因为static声明变量的生命周期其实是和APP的生命周期一样的,有点类似于Application。如果大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销,直至挂掉。static一般用来修饰基本数据类型或者轻量级对象,尽量避免修饰集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。

最后再来个扩展阅读...

GC回收的对象?

如果GC发现一个或一组对象为不可到达状态,则将该对象从内存中回收。也就是说,一个对象不被任何引用所指向,则该对象会在被GC发现的时候被回收;另外,如果一组对象中只包含互相的引用,而没有来自它们外部的引用(例如有两个对象A和B互相持有引用,但没有任何外部对象持有指向A或B的引用),这仍然属于不可到达,同样会被GC回收。

什么是WeakReference? OOM问题解决方法?

WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用),该对象就会在被GC检查到时回收掉。如果对内存的开销比较关注的APP,可以考虑使用WeakReference,当GC回收扫过这块内存区域时就会回收;如果不是那么关注的话,可以使用SoftReference,它会在内存申请不足的情况下自动释放,同样也能解决OOM问题。同时Android自3.0以后也推出了LruCache类,使用LRU算法就释放内存,一样的能解决OOM,如果要兼容3.0以下的版本,需要导入v4包。

时间: 2024-11-10 01:27:59

关于安卓性能和内存的相关文章

安卓性能优化 视频

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha 安卓-性能优化之内存泄漏-原创-高清视频-爱奇艺 ========= Android手机内存管理与性能优化视频教程下载 下载:http://pan.baidu.com/s/1skkSdI1密码:kdcs 01.Dalvik介绍及其优势和文件格式 02.基于栈与基于寄存器比较 03.DEX与ODEX文件格式和ODEX化详解 04.GC垃圾回收机制 05.内存监测分析工具DDMS介绍 0

为什么安卓系统大内存却比IOS卡顿

内存管理,深受Windows下释放内存加速系统毒害,人们误以为内存剩余越多越好.其实不然,当一个应用程序退出时,OS不应该立即释放其内存,而是要等到内存不够用了才开始释放.Why? (1)有些应用频繁启动关闭,启动时,如果程序已经加载到了内存中,那么启动速度将飞快.相反的,如果不在内存,就需要从外部存储里面读取,需要等待,比较慢. (2)内存使用率越高越好.内存是易失性存储设备,当断电了数据就会消失,使用时还需要不断刷新.刷新0,还是1,对于功率是不会产生大的影响,所以也不会存在,内存使用率低,

安卓中的内存泄漏

因为安卓是基于java语言的,所以我们先来看一看java中的内存泄漏,然后在此基础上来谈谈安卓中的内存泄漏. 一java中的内存泄漏: java中的内存泄漏主要是指在堆中分配的内存,明明已经不需要的时候,还仍然保留着访问它的引用,导致GC回收不能及时回收(关于GC回收不做过多赘述),导致这种情况出现的最主要原因是长生命周期的对象持有短生命周期对象的引用,导致短生命周期的对象明明已经不需要却无法被GC回收,从而导致内存泄漏.主要包括以下几种情况: 1在一个类中创建了一个非静态内部类的静态实例,如下

如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏、内存膨胀和频繁的垃圾回收

了解如何使用 Chrome 和 DevTools 查找影响页面性能的内存问题,包括内存泄漏.内存膨胀和频繁的垃圾回收. TL;DR 使用 Chrome 的任务管理器了解您的页面当前正在使用的内存量. 使用 Timeline 记录可视化一段时间内的内存使用. 使用堆快照确定已分离的 DOM 树(内存泄漏的常见原因). 使用分配时间线记录了解新内存在 JS 堆中的分配时间. 概览 在 RAIL 性能模型的精髓中,您的性能工作的焦点应是用户. 内存问题至关重要,因为这些问题经常会被用户察觉. 用户可通

安卓性能测试之应用内存泄漏总结

pre { direction: ltr; color: rgb(0, 0, 10); text-align: left } pre.western { font-family: "Liberation Serif", serif; font-size: 12pt } pre.cjk { font-family: "Droid Sans Fallback"; font-size: 12pt } pre.ctl { font-family: "Droid S

安卓性能优化手册

本手册适合至少有初级经验的开发者查阅或复习相关知识使用,新手可能会看不懂. 1.java代码优化 1.1安卓如何执行代码 dvm:.java->.class->.dex->.apk 优化斐波那契数列: 斐波那契数列的递推公式是f(n)=f(n-1)+f(n-2),特征方程为:x2=x+1,解该方程得(1+sqrt(5))/2,(1-sqrt(5))/2.所以f(n)=Ax1n+Bx2n,带入f(0)=0,f(1)=1得A=sqrt(5)/5,B=-sqrt(5)/5.则f(n)求出. B

浅谈javascript性能-管理内存

上次说到,javascript脚本到底应该放在哪里?用什么用处? 以下2点: 在Html.Body部分中的JS会在页面加载的时候执行.即-用户触发一个事件的时候执行的脚本.eg:onload事件... 在Html.Head部分中的JS会在被调用的时候执行.即-常被用来生成页面的内容. 总而言之:将Javascript标识放置<head>...</head>在头部中间,此时,*.js文件的提前调用:也就是说代码放在<head>区域载入的时候,就同时载入了代码,你再<

iOS 25个性能优化/内存优化常用方法

1. 用ARC管理内存 ARC(Automatic ReferenceCounting, 自动引用计数)和iOS5一起发布,它避免了最常见的也就是经常是由于我们忘记释放内存所造成的内存泄露.它自动为你管理retain和release的过程,所以你就不必去手动干预了.忘掉代码段结尾的release简直像记得吃饭一样简单.而ARC会自动在底层为你做这些工作.除了帮你避免内存泄露,ARC还可以帮你提高性能,它能保证释放掉不再需要的对象的内存. 2. 在正确的地方使用 reuseIdentifier 一

MySQL性能优化-内存参数配置

Mysql对于内存的使用,可以分为两类,一类是我们无法通过配置参数来配置的,如Mysql服务器运行.解析.查询以及内部管理所消耗的内存:另一类如缓冲池所用的内存等. Mysql内存参数的配置及重要,设置不当很有可能会造成很大的性能问题甚至是服务器宕机,内存相关参数的配置需要考虑以下两点: (1)确定可以使用的内存上限: (2)确定Mysql每个连接使用的内存: 在Mysql的体系中(如下图所示),简单来说可以分为两层,第一层代表各种可以用过Mysql连接协议连接到Mysql的客户端,例如PHP.