Android内存管理、优化

RAM对于软件开发环境而言是有价值的资源,但它对受限于物理内存限制的操作系统具有更大的价值。即使Android Runtime和Dalvik virtual machein执行常规的垃圾回收,但这并不意味着你可以忽略app在何时何地指派和释放内存。你仍然需要去避免产生内存泄露。比如长期持有静态成员变量常常引起内存泄露,你应该在合适的时间,比如在生命周期回调函数处释放一些引用对象,来避免内存泄露的发生。

这篇文章将阐述怎样在app中主动的降低内存消耗。关于java编程中清理资源的一般实践,请参照其他关于资源引用管理的书籍或者在线文档。如果你想分析一个运行中app的内存情况,请阅读文章“Tools for analyzing RAM usage”。如果你想知道AndroidRuntime和Dalvik virtual machine如何管理内存,请参阅文章“Overview of Android Memory Management”。

监控内存的使用情况:

Androidframework和Android Studio能够帮助你分析和调整app的内存使用情况。Android framework 暴露了几个在app运行时动态降低内存消耗的API。Android Studio 提供了几个观察app内存使用情况的工具。

分析内存使用的工具:

想要解决app产生的内存问题,我们首先得知道内存问题由什么引起。Android Studio提供了如下几个分析内存的工具:

1.“MemoryMonitor”展示app 在一个单独回话过程中如何指派内存。这个工具会实时绘制包括垃圾回收事件等可用内存和已占用内存的模拟图像。在程序运行时,你仅仅能通过初始化一个垃圾回收事件,获取Java 堆快照。这个Memory Monitor 的输出图像能帮你定位到app在什么地方因产生过多的垃圾回收事件导致程序变慢。

关于Memory  Monitor的具体使用方法,请参阅“ViewingHeap Updates”

2.“AllocationTracker”能够让我们详细的跟踪到app如何分配内存。这个工具能够记录app的内存分配情况,而且能够列出所有被分配的对象以及分析快照。用这个工具你能够追踪到分配太多对象的代码。

关于AllocationTracker的具体使用方法,请参阅“Allocation Tracker Walkthrough”

在合适的时间释放内存     

Android设备在运行时的可用内存会根据物理内存总量以及用户操作方法不断变化。当系统内存有压力的情况下会发送信号来通知程序。app应监听这些通知,并依据监听结果来调整内存使之使用适度。

用APIComponentCallbacks2 来响应app生命周期事件和 设备事件,根据监听相关信号的结果调整内存的使用。方法onTrimMemory()能够监听app运行在前台或后台时候的相关内存事件。

在Activity中,实现回调方法onTrimMemory()来监听这些内存变化的事件,如下代码片段:

import android.content.ComponentCallbacks2;
// Other import statements ...

publicclassMainActivityextendsAppCompatActivity
    implementsComponentCallbacks2{

// Other activity code ...

/**
     * Release memory when the UI becomes hidden or whensystem resources become low.
     * @param level the memory-related event that wasraised.
     */
    publicvoid onTrimMemory(int level){

// Determine which lifecycle or systemevent was raised.
        switch(level){

caseComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:

/*
                  Release any UI objects that currently hold memory.

Theuser interface has moved to the background.
                */

break;

caseComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
            caseComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
            caseComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:

/*
                  Release any memory that your app doesn‘t need to run.

Thedevice is running low on memory while the app is running.
                   Theevent raised indicates the severity of the memory-related event.
                   Ifthe event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                   beginkilling background processes.
                */

break;

caseComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
            caseComponentCallbacks2.TRIM_MEMORY_MODERATE:
            caseComponentCallbacks2.TRIM_MEMORY_COMPLETE:

/*
                  Release as much memory as the process can.

Theapp is on the LRU list and the system is running low on memory.
                   Theevent raised indicates where the app sits within the LRU list.
                   Ifthe event is TRIM_MEMORY_COMPLETE, the process will be one of
                   thefirst to be terminated.
                */

break;

default:
                /*
                  Release anynon-critical data structures.

The appreceived an unrecognized memory level value
                  from thesystem. Treat this as a generic low-memory message.
                */
                break;
        }
    }
}

备注:回调方法onTrimMemory()在Android4.0中添加。更早的版本,能用回调方法onLowMemory()作为替代,大约和 TRIM_MEMORY_COMPLETE 事件相同。

检查你需要用到的内存:

为了支持多进程,android为每一个app设置了一个堆内存上限。不同设备为app分配的具体上限值,取决于RAM的总大小。如果程序到达了这个上限值还想占用更多的内存,系统将会抛出异常 OutOfMemoryError。

为了避免内存溢出,我们可以查询当前设备为app指定的内存上限值。我们可以通过调用方法getMemoryInfo()来查询当前系统的这个属性值。这个方法将会返回一个包含可用内存、总内存、内存临界值(如果系统可用内存低于这个临界值将会杀掉部分进程)等当前设备状态的ActivityManager.MemoryInfo对象。ActivityManager.MemoryInfo也提供了一个boolean类型的字段lowMemory,记录当前设备是否运行在低内存情况下。

下面的代码片段展示了一个使用geMemoryInfo()方法的例子:

        publicvoid doSomethingMemoryIntensive(){

    // Before doing something that requires a lot of memory,    // check to see whether the device is in a low memory state.    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();

    if(!memoryInfo.lowMemory){        // Do memory intensive work ...    }}

// Get a MemoryInfo object for the device‘s current memory status.privateActivityManager.MemoryInfo getAvailableMemory(){    ActivityManager activityManager =(ActivityManager)this.getSystemService(ACTIVITY_SERVICE);    ActivityManager.MemoryInfo memoryInfo =newActivityManager.MemoryInfo();    activityManager.getMemoryInfo(memoryInfo);    return memoryInfo;}

使用高效内存的代码结构

一些android特性、java类、代码结构趋向于用更多的内存。我们可以通过选择更高效的替代方案来使我们app用最少的内存。

少用Services

在不必要的时候运行一个服务,是app最糟糕的内存管理错误之一。如果app需要运行一个服务在后台执行工作,除非有必要的任务,否则不要一直运行。当Service完成它的任务后一定要记得停这个Service。否则,可能一不留神就产生一个内存泄露。

开启一个Service后,系统喜欢为这个service开启一个进程使其持续运行。这种情况使Service非常耗费资源,因为其他进程无法使用被service所在进程占用的RAM资源。系统缓存在LRU catche的进程数量减少后,使app运行效率降低。更有甚者,当内存紧张,系统无法维持足够的进程支持所有service的时候,service可能被销毁。

通常情况下我们应该避免长期使用service,因为它们会一直占用内存。我们推荐使用可供选择的类比如JobScheduler 去调度后台进程。

关于如何使用JobScheduler调度后台进程,参阅“BackgroundOptimizations”

使用最优化的数据容器:

编程语言提供的部分类对于移动设备不是最优化的。例如一般的HashMap实现能使很多内存低效率工作,因为它需要为每一个映射生成一个单独的条目对象。

Androidframework包含几个最优化的数据容器,包括SparseArray,SparseBooleanArray,LongSparseArray。例如 AparseArray之所以更高效,是由于它们避免系统的需要,对key(有时候也对value)进行自动装箱。

如果有必要,我们可以总是选择原始的数组作为最精炼的数据结构。

谨慎使用抽象代码:

开发者通常仅仅把抽象当成一种好的编程实践,因为抽象可以提升代码的灵活性和可维护性。然而,抽象伴随着巨大的资源耗费而来:通常它们会需要更多的时间和更多的内存,需要执行相当数量的代码,花费更多的时间和内存。所以如果抽象不能带来巨大好处,我们应该尽可能去避免。

例如:枚举需要消耗的内存通常是静态变量的两倍多。我们应该严格避免在android上使用枚举。

使用nanoprotobufs序列化数据

Protocol buffers 是一个谷歌设计的具有语言中立、平台中立、可扩展、用来序列化结构数据的类似于XML的机制,但其比XML快,比XML小,比XML简单。如果你决定使用protobufs处理数据,那么我们应该在客户端代码中一直使用nano protobufs。普通的protobufs通常生成非常多的代码,这样会引起app产生很多问题,比如占用更多内存、APK尺寸大幅度增加、运行缓慢。

更多运行,请参阅文章"protobuf readme"中"Nanoversion"的段落。

避免内存抖动

就像上文提醒到的,GC事件通常不会影响app的性能。然而一些发生在短时间内的GC事件能迅速耗尽你的帧像时间。系统花费在GC上的时间越多,那么它处理其他诸如渲染或者音频流的事件。

通常情况下下,内存抖动能引起大量的的GC事件。在实践中,内存抖动描绘的是,在一个给定时间内,分配临时对象的数目。

例如,你可能通过for循环分配多个临时对象。或者在一个View的onDraw方法中创建一个Paint或者Bitmap对象。如上两个例子,app迅速地创建了大量的大体积对象。年青一代的这些行为将会迅速的消耗所有的可用内存,强制一个垃圾回收事件产生。

当然,在你修复内存抖动之前,需要先发现导致内存抖动发生的代码所在地方。请用在文章"Analyze your RAM usage"中讨论的工具。

一旦你识别代码出现问题的区域,试着降低处于性能临界区域的配置数目。考虑移除内循环的东西,或者将它们移到基于分配结构的 Factory(参阅文章:)。

降低内存占有量——累赘的资源和库

一些资源和库在你的代码能在你不知道的情况下贪婪的消耗内存。apk的大小、包括第三方库以及嵌入的资源、能够影响app消耗的内存量。我们能通过移除代码中多余的、不必要的、臃肿的组件、资源或者库的方式来改善app的内存消耗。

降低apk大小

我们能通过降低apk大小的方式显著的降低app的内存使用情况。bitmap尺寸、资源、动画的帧、第三方库都能导致apk变大。android sdk 和 android studio 提供了多种工具来帮助我们降低资源和外部依赖的尺寸。

关于降低apk大小的更多信息,参阅"ReduceAPK Size"。

         如果需要依赖注入,建议使用Dagger

依赖注入框架能够简化你写的代码以及为测试和测试改变提供一个适配的环境。如果你打算在app中用一个依赖注入框架,考虑使用Dagger2(参阅https://google.github.io/dagger/).

Dagger不会用反射扫描app代码。它是静态的、编译时实现的框架,这意味着在运行时没有不必要的运行时花费或者内存使用。

其他的那些依赖注入框架通过扫描代码和注解,运用反射去初始化进程。这个产生的进程会需要大量的CPU周期以及内存,能在app启动时引起一个显而易见的延迟。

谨慎使用依赖库:

外部库的代码通常不是为手机环境所写,使其在手机客户端上工作时可能是很低效的。当我们决定用一个依赖库,可能需要针对移动设备进行优化。在决定真正使用这个依赖库之前,应预先做好针对移动设备优化的计划,依据代码尺寸和内存大小进行分析。

即使一些移动优化的库也能由于不同的实现引起问题。例如,当一个库使用了micro protobufs而一个库使用了nano protobufs,这样app中有两个不同的 protobuf实现。当问题发生时,原因可能是logging,analytics,image loading frameworks,caching或者一些其他我们无法预料的事情的不同实现导致的。

尽管ProGuard(参阅:https://developer.android.com/studio/build/shrink-code.html)能根据正确的标记移除APIs和资源,但是它不能移除一个库大型的依赖。你在库中想要的特性可能需要低版本的依赖....

翻译整理,更多内容,敬请期待....

时间: 2024-10-23 09:47:43

Android内存管理、优化的相关文章

Android内存性能优化(内部资料总结)

刚入门的童鞋肯能都会有一个疑问,Java不是有虚拟机了么,内存会自动化管理,我们就不必要手动的释放资源了,反正系统会给我们完成.其实Java中没有指针的概念,但是指针的使用方式依然存在,一味的依赖系统的gc,很容易就造成了内存的浪费.   Java基于垃圾回收的内存机制 Java的内存管理机制会自动回收无用对象所占用的内存,减轻手工管理内存的负担 1.C/C++: 从申请.使用.释放都需要手工管理 2.Java:无用的对象的内存会被自动回收 什么样的对象是无用的对象 1.Java通过引用来操作一

Android内存管理之道

相信一步步走过来的Android从业者,每个人都会遇到OOM的情况.如何避免和防范OOM的出现,对于每一个程序员来说确实是一门必不可少的能力.今天我们就谈谈在Android平台下内存的管理之道,开始今天的主题之前,先再次回顾两个概念. 内存泄漏:对象在内存heap堆中中分配的空间,当不再使用或没有引用指向的情况下,仍不能被GC正常回收的情况.多数出现在不合理的编码情况下,比如在Activity中注册了一个广播接收器,但是在页面关闭的时候进行unRegister,就会出现内存溢出的现象.通常情况下

android 内存管理研究

1. 内存管理基础知识 http://www.cnblogs.com/xingfuzzhd/p/3485924.html   1. mImageView.setImageResource(R.drawable.my_image); 这段代码会调用 BitmapFactory.decodeStream() 生成一个 Bitmap.所以不要以为它比自己创建 Bitmap 节省内存. 3.  实际测试: 我使用了多种调用图片的方法来测试: 第一种: // 直接载入资源 idImageView imag

Android内存管理机制详解 (zhuan)

http://www.2cto.com/kf/201212/175786.html 与windows内存区别 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理.主要特点是,无论物理内存有多大,Linux都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能.而Windows是只在需要内存时,才为应用程序分配内存,

Android内存性能优化(内部资料总结) eoe转载

刚入门的童鞋肯能都会有一个疑问,Java不是有虚拟机了么,内存会自动化管理,我们就不必要手动的释放资源了,反正系统会给我们完成.其实Java中没有指针的概念,但是指针的使用方式依然存在,一味的依赖系统的gc,很容易就造成了内存的浪费. Java基于垃圾回收的内存机制 Java的内存管理机制会自动回收无用对象所占用的内存,减轻手工管理内存的负担 1.C/C++: 从申请.使用.释放都需要手工管理 2.Java:无用的对象的内存会被自动回收 什么样的对象是无用的对象 1.Java通过引用来操作一个具

[Android Memory] Android内存管理、监测剖析

转载自:http://blog.csdn.net/anlegor/article/details/23398785 Android内存管理机制: Android内存管理主要有:LowMemory Killer机制,Ashmem,PMEM/ION及Native内存和Dalvik内存管理管理和JVM垃圾回收机制. LowMemory Killer机制: 源码位置drivers/staging/Android/lowmemorykiller.c Android是一个多任务系统,也就是说可以同时运行多个

Android 内存管理机制详解

??嵌入式设备的一个普遍特点是内存容量相对有限.当运行的程序超过一定数量时,或者涉及复杂的计算时,很可能出现内存不足,进而导致系统卡顿的现象.Android 系统也不例外,它同样面临着设备物理内存短缺的困境.对于已经启动过一次的Android程序,再一次启动所花的时间会明显减少.原因在于Android系统并不马上清理那些已经"淡出视野"的程序(比如你调用Activity.finish退出UI界面).它们在一定的时间里仍然驻留在内存中.这样做的好处是明显的,即下一次启动不需要再为程序重新

Android 内存泄漏优化汇总

android内存泄漏优化摘要 博客分类: android android内存溢出OutOfMemoryError . android移动应用程序的内存分配一般是8凯瑟琳约,不正确地假定处理内存处理非常easy创建OutOfMemoryError.我们的产品是最常见的错误是OutOfMemoryError的异常, 在解决这个异常时在网上发现非常多关于OutOfMemoryError的原因的介绍. OutOfMemoryError主要由下面几种情况造成: 1.数据库的cursor没有关闭. 操作S

Android内存管理的原理--进程管理

Android内存管理的原理--进程管理 Android采取了一种有别于 Linux 的进程管理策略,有别于Linux的在进程活动停止后就结束该进程,Android把这些进程都保留在内存中,直到系统需要更多内存为止.这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度. 那Android什么时候结束进程?结束哪个进程呢? 之前普遍的认识是Android是依据一个名为LRU(last recently used 最近使用过的程序)列表,将程

移动端测试===Android内存管理: 理解App的PSS

Android内存管理: 理解App的PSS 原文链接:http://www.littleeye.co/blog/2013/06/11/android-memory-management-understanding-app-pss/ 当在应用程序上运行Little Eye时,在内存视图中,会报告有关应用程序内存的3个重要统计信息. Dalvik内存使用情况,即Java堆消耗的内存量,Native内存,即JVM外部进程使用的内存量. 然后是第三个统计量,被称为"PSS". 什么是PSS?