关于Android内存优化你应该知道的一切

简介:

介绍

Android系统中,内存分配与释放分配在一定程度上会影响App性能的—鉴于其使用的是类似于Java的GC回收机制,因此系统会以消耗一定的效率为代价,进行垃圾回收。 
在中国有句老话:”由俭入奢易,由奢返俭难”。而此谚语也似乎正适应于Android的内存使用。GC回收机制给程序员省去了像C语言程序员那样手动释放内存的工作,但是也带来了一系列的”雷”—动辄内存泄漏,再甚者稍微不慎就会OOM。 
这篇文章将会介绍Android的内存管理机制并解释几种在此机制下对内存有影响的几个比较关键的因素。另外,还会介绍如何提高内存管理、检测并避免内存泄漏,以及如何分析内存分配情况

Here we go!!

Android内存管理机制

Android内存模型并没有交换空间(swap space)的概念,而是使用分页(paging)和内存映射(memory-mapping)管理内存,这意味着不管是分配新的对象还是使用已有的映射页这些内存仍然被占据在RAM里而不能被扇出。因此完全释放你app内存的唯一方式是释放对象引用以便于能被垃圾回收器回收。 
Dalvik虚拟机为每一个App分配相应大小的可用内存空间,从2M开始到32M(此最大值根据不同的厂商一般会有不同),不可否认,在当前国内各大手机厂商疯狂的拼硬件的时代,这个每个App的可用内存甚至被提高到了256M,这有效的避免了很多OOM的情况,但是如果程序员因此就不管内存管理任意而为,会为此付出严重代价的(App高卸载率). 
Android系统会将在后台运行的App进程保存在一个LRU cache中(不懂的自行百度)。当系统内存紧张时,它会根据LRU的策略kill掉一些优先级比较低的进程。当然,究竟哪一个App是当前占用内存最大的程序也是它kill进程时所考虑的一个因素。如果你希望自己的App在后台运行时能尽可能长的”活着”,不被系统kill掉,就要好好的思考如何避免被kill。比如在App转到后台运行之前,尽可能的将没有用的内存给释放掉,这样会减少Android系统打印错误日志甚至终止App的可能性。

如何提高Android内存使用

Android系统是世界上使用率最高的手机系统。每年都有成千上万的年轻人转入到开发Android系统的行列中,但是这些人中,能真正写出稳定、可扩展性强的代码的还是少数。

以下是提高内存使用的几条建议:
  1. 慎用桥接模式,虽然从程序的设计角度来看,抽象能够帮助我们创建更加灵活的软件架构。但是在手机系统中,这种设计模式有可能会造成很多副作用。除非大有必要,否则尽量不要用桥接模式
  2. 避免使用枚举Enum,一个Enum分配的空间是一个普通常量的两倍,因此尽量少使用枚举
  3. 试着使用Android框架优化后的数据容器,譬如:SparseArray, SparseBooleanArray, 以及 LongSparseArray containers. 使用这些类来替代HashMap的使用。原因是传统的 HashMap 在内存上的实现十分的低效,因为它需要为 HashMap 中每一项在内存中建立映射关系. 另外, SparseArray类非常高效因为它避免了对key和value的自动封箱. 万事都有两面性,这些个被优化过的容器也不例外,千万记住SparseArray等容器并不适应于内部元素很多的集合,当集合的长度超过1000条时,使用SparseArray进行增删改查的效率远比HashMap低
  4. 避免创建不需要的对象。对于生命周期较短的临时变量,尽量想办法规避掉每次都要去创建它,这样GC回收被强制调用机会就会更少,留给Android系统进行UI渲染或者音频加载的时间就会更多,从而避免了卡顿现象
  5. 检测App内存中的可用堆的大小,在代码中可以通过动态的调用ActivityManager::getMemoryClass()方法来查询你的App中的可用内存堆大小。如果系统检测到需要分配的内存大小超过了此值,则会抛出OOM错误
  6. **可以适当适应onTrimMemory回调方法。OnTrimMemory 回调是 Android 4.0 之后提供的一个API,这个 API 是提供给开发者的,它的主要作用是提示开发者在系统内存不足的时候,通过处理部分资源来释放内存,从而避免被 Android 系统杀死。这样应用在下一次启动的时候,速度就会比较快。—详情请参阅Android内存优化—OnTrimMemory
  7. 当使用Service应当小心小心再小心!当你需要启动一个服务在后台执行一项任务时,应当在其完成工作之后尽快的停止此服务。可以考虑使用IntentService—当在子线程完成耗时操作之后,IntentService会自动停止并结束自身。然而在实际开发中经常会碰到需要服务去执行一项耗时比较长的任务,比如:音乐播放器,下载APP等等。像这样的应用可以分隔为两个进程:一个进程负责 UI 工作, 另外一个则在后台服务中运行其它的工作. 在AndroidManifest 文件中为各个组件申明 android:process 属性就可以分隔为不同的进程。注意一点:在后台运行的Service绝对不能处理或者持有任何UI,否则系统可能会分配双倍甚至三倍的空间来维护UI资源!!
  8. 当你加载 bitmap 时, 需要根据当前设备的分辨率加载相应分辨率的bitmap进入内存,如果下载下来的原图分辨率比设备分辨率高则要压缩它. 要小心bitmap的分辨率增加后所占用的内存也要进行相应的增加(平方级increase2的增长), 因为它是根据x和y的大小来增加内存占用的
  9. 使用代码混淆工具 ProGuard 通过去除没有用的代码和通过语义模糊来重命名类, 字段和方法来缩小, 优化和混淆你的代码. 使用它能使你的代码更简洁, 更少量的RAM映射页.如果构建apk后你没有做后续的任何处理(包括根据你的证书进行签名), 你必须运行 zipalign 工具为你的apk进行优化, 如果不这样做会导致你的应用使用更多的内存,zipalign之后像资源这样的东西不会再从apk中映射(mmap)入内存.注意:goole play store 不接受没有进行zipalign的apk

针对以上几条,后续会单独再post几篇blog单独讲解。

如何避免内存泄漏

程序员在分配内存时如果考虑到了上述9条建议,或许会给App在效率上带来不小的收益,并且可以在后台时依然坚挺(更持久!)。 但是这一切的努力都会因为一个叫做内存泄漏的东东而萎了! 这玩意就如同可乐的存在一样,少喝一点还能扛得住,但是多了的话。。你懂得! 以下是几个常见的造成内存泄漏的情况:

  • 当查询完数据库之后,及时关闭Cursor对象。
  • 记得在Activity的onPause方法中调用unregisterReceiver()方法,解注册广播
  • 避免Content内存泄漏,比如在4.0.1之前的版本上不要讲Drawer对象置为static。当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个callback成员变量,上面的例子中静态的sBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其他更多对象的引用。sBackground生命周期要长于Activity。当屏幕旋转时,Activity无法被销毁,这样就产生了内存泄露问题。
  • 尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的声明周期长于Activity的声明周期时,会导致Activity无法被GC正常回收掉。
  • 谨慎使用线程Thread!!这条是很多人会犯的错误: Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为null或者用户直接kill进程操作。所以当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉
  • 使用Handler时,要么是放在单独的类文件中,要么就是使用静态内部类。因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露–详情请参阅Android中Handler引起的内存泄露

如何分析内存的使用情况

在Mac终端(windows的cmd)中,可以使用adb logcat命令来查看或者统计内存的具体使用情况,另外还可以指定包名来查看相应App的内存使用情况。除此之外,还可以使用三方的工具来分析Android内存的使用情况,比如:DDMS、MAT(Memory Analyzer tool).

在adb logcat中,通常能看到GC相关的log如下图所示

Dalvik GC log

GC_Reason 触发GC回收的原因,可能包含以下几种情况:

  • GC_FOR_ALLOC, 这个是说我们的应用尝试去分配内存而这时候和heap已经快满了(不够用了),这个时候系统会把我们的应用停下来然后进行内存回收,通常heap size会增大
  • GC_CONCURRENT,这个应该的当我们的Heap size 快要被填满的时候触发的一个并发的内存回收
  • GC_EXPLICIT,这个是主动调用系统gc方法触发的GC(在DDMS 点击GC就可以看到)
  • GC_HPROF_DUMP_HEAP 我们在做内存分析创建HPROF(MAT可以分析该文件)的时候会打印

Amount feed 表示本次垃圾收集释放了多少内存

Heap_stats 当前空闲内存占总内存的百分比

External memory stats 表示API 10及以下的外部分配内存,已分配内存/导致垃圾回收的阈值

Pause_time 应用暂停的时间

通常情况下,生成的GC log越大,表示内存的分配与释放发生的频率越高,这种情况下往往会非常影响用户体验!

使用DDMS查看并追踪堆内存的分配情况

通过DDMS,程序员可以很轻松的检测指定进程的内存分配情况。你可以通过“Heap”标签查看最新的实时的堆内存信息,这样可以帮助你辨别出究竟是哪一个操作最有可能造成大量的内存分配。 “Allocation Tracker” 标签显示的是最近所有的内存分配—包含分配对象的类型,是在哪个线程中分配等信息。一下图片演示的是使用DDMS展示进程信息—包含了当前进程、对内存分配统计信息。

heap_stat




    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6632775.html,如需转载请自行联系原作者


相关文章
|
7月前
|
缓存 编解码 Android开发
Android内存优化之图片优化
本文主要探讨Android开发中的图片优化问题,包括图片优化的重要性、OOM错误的成因及解决方法、Android支持的图片格式及其特点。同时介绍了图片储存优化的三种方式:尺寸优化、质量压缩和内存重用,并详细讲解了相关的实现方法与属性。此外,还分析了图片加载优化策略,如异步加载、缓存机制、懒加载等,并结合多级缓存流程提升性能。最后对比了几大主流图片加载框架(Universal ImageLoader、Picasso、Glide、Fresco)的特点与适用场景,重点推荐Fresco在处理大图、动图时的优异表现。这些内容为开发者提供了全面的图片优化解决方案。
305 1
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
395 1
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
542 16
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
422 31
|
Java 测试技术 Android开发
Android性能测试——发现和定位内存泄露和卡顿
本文详细介绍了Android应用性能测试中的内存泄漏与卡顿问题及其解决方案。首先,文章描述了使用MAT工具定位内存泄漏的具体步骤,并通过实例展示了如何分析Histogram图表和Dominator Tree。接着,针对卡顿问题,文章探讨了其产生原因,并提供了多种测试方法,包括GPU呈现模式分析、FPS Meter软件测试、绘制圆点计数法及Android Studio自带的GPU监控功能。最后,文章给出了排查卡顿问题的四个方向,帮助开发者优化应用性能。
1063 4
Android性能测试——发现和定位内存泄露和卡顿
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
209 2
|
缓存 监控 Android开发
构建高效的Android应用:从内存优化到用户体验
【7月更文挑战第57天】 在竞争激烈的移动市场中,一个高效、流畅且具有优秀用户体验的Android应用是成功的关键。本文将深入探讨如何通过内存管理和界面优化来提升应用性能,包括实用的编程技巧和策略,以及如何利用Android系统提供的工具进行调试和性能监控。读者将学习到如何识别和解决常见的性能瓶颈,以及如何设计出既美观又实用的用户界面。

热门文章

最新文章