Android如何管理内存
android不提供内存交换的空间,android通过分页和内存映射的方式管理内存。因此任何你new的对象或者连接的内存映射(比如打开文件)都会驻留在内存。释放这些内存唯一的方式就是,释放对这些对象的和文件引用的持有,以便GC程序能回收这部分内存。(有一种特殊情况,比如代码,系统可以明确指定用哪一块的内存(这个不是很明白))。
共享内存
In order to fit everything it needs in RAM, Android tries to share RAM pages across processes. It can do so in the following ways:
为了满足内存需要,android试图通过进程来共享内存页。android通过如下方式实现
- Each app process is forked from an existing process called Zygote. The Zygote process starts when the system boots and loads common framework code and resources (such as activity themes). To start a new app process, the system forks the Zygote process then loads and runs the app's code in the new process. This allows most of the RAM pages allocated for framework code and resources to be shared across all app processes.
- 每个应用程序进程都会与一个叫受精卵进程的进行连接而来。受精卵进程在系统启动时启动,负责加载共同框架代码和资源(如Activity的主题)。一个应用的进程刚启动时,系统会fork(Linux中的概念)到受精卵进程,然后在一个新的进程用运行应用的代码,这使得大多数为框架代码和资源分配的内存页能被所有应用程序共享过程。具体需要了解Linux的fork截止。简单的理解就是通过这样一种手段将公用的资源和代码,只在内存中存在一份,大大的减少了内存的使用量。
- Most static data is mmapped into a process. This not only allows that same data to be shared between processes but also allows it to be paged out when needed. Example static data include: Dalvik code (by placing it in a pre-linked
.odex
file for direct mmapping), app resources (by designing the resource table to be a structure that can be mmapped and by aligning the zip entries of the APK), and traditional project elements like native code in.so
files.
- In many places, Android shares the same dynamic RAM across processes using explicitly allocated shared memory regions (either with ashmem or gralloc). For example, window surfaces use shared memory between the app and screen compositor, and cursor buffers use shared memory between the content provider and client.
- 在许多地方,android通过预先明确的指定共享内存区域,通过这些区域共享内存数据。比如surfaces使用应用与屏幕绘制器间的共享内存,cursor缓冲区使用content 提供者与客户端的共享内存。
Due to the extensive use of shared memory, determining how much memory your app is using requires care. Techniques to properly determine your app's memory use are discussed in Investigating Your RAM Usage.
因为内存的共享大量存在,需要谨慎的决定使用多少内存,技术上到达这个目的可以参考Investigating Your RAM Usage.(分析你的内存使用这个链接)
Allocating and Reclaiming App Memory
Here are some facts about how Android allocates then reclaims memory from your app:
以下是android分配和回收内存的实例:
Dalvik堆栈为每个进程单独限定虚拟内存堆栈的范围,这个范围值定义了内存堆栈的逻辑大小,随应用的内存使用量增长(上限是系统给每个应用定义的最大值)。
内存堆栈的逻辑大小与实际使用大小并不相同,分析你应用内存堆栈的时候,会发现,android会计算一个叫做“比例设置大小”PSS,这个值会计算与其他进程共享dirty和clean页的大小不过仅仅有多少进程共享这个内存的比例值而已。系统会根据PSS的值决定你应用物理内存占用。详细见Investigating your ram usage帮助文档。
- The Dalvik heap for each process is constrained to a single virtual memory range. This defines the logical heap size, which can grow as it needs to (but only up to a limit that the system defines for each app).
- The logical size of the heap is not the same as the amount of physical memory used by the heap. When inspecting your app's heap, Android computes a value called the Proportional Set Size (PSS), which accounts for both dirty and clean pages that are shared with other processes—but only in an amount that's proportional to how many apps share that RAM. This (PSS) total is what the system considers to be your physical memory footprint. For more information about PSS, see the Investigating Your RAM Usage guide.
- The Dalvik heap does not compact the logical size of the heap, meaning that Android does not defragment the heap to close up space. Android can only shrink the logical heap size when there is unused space at the end of the heap. But this doesn't mean the physical memory used by the heap can't shrink. After garbage collection, Dalvik walks the heap and finds unused pages, then returns those pages to the kernel using madvise(这个函数可以对映射的内存提出使用建议,从而提高内存). So, paired allocations and deallocations of large chunks should result in reclaiming all (or nearly all) the physical memory used. However, reclaiming memory from small allocations can be much less efficient because the page used for a small allocation may still be shared with something else that has not yet been freed.
- Dalvik堆栈不计算堆栈的逻辑大小意味着android不会通过碎片整理的方式释放堆栈的空间,android只能通过删除堆栈的尾部不用的空间来减少堆栈的大小,但这并不是说物理内存不能被压缩(不能从堆栈中间删除数据)。GC回收器会浏览堆栈,并把堆栈中不用的页空间回收到内核通过madvise这个函数(这个函数可以对映射的内存提出使用建议,从而提高内存)。所以,成对分配与回收大块的数据可以全部(几乎全部)回收物理内存。然而用同样的方式回收小块数据的效率就低了。因为小块数据可能还被其他的使用,且引用放还没有释放这个引用。
-
Restricting App Memory 应用内存限制
To maintain a functional multi-tasking environment, Android sets a hard limit on the heap size for each app. The exact heap size limit varies between devices based on how much RAM the device has available overall. If your app has reached the heap capacity and tries to allocate more memory, it will receive an
OutOfMemoryError
.In some cases, you might want to query the system to determine exactly how much heap space you have available on the current device—for example, to determine how much data is safe to keep in a cache. You can query the system for this figure by calling
为了维护多任务的运行,android 为每个应用设置了一个硬性的内存堆栈限制,一旦应用使用的内存超过这个值就会报OUtOfMemoryError的错误。getMemoryClass()
. This returns an integer indicating the number of megabytes available for your app's heap. This is discussed further below, under Check how much memory you should use. - 一些情况下,你的应用可能需要知道当前运行系统环境对堆栈内存的限制大小是多少,从而做出合理的操作,不如缓存多少数据,使用高清图片还是普通图片。可以通过getMemoryClass()的方式获取这个限制的大小,详见Check how much memory you should use
-
Switching Apps 应用间切换
Instead of using swap space when the user switches between apps, Android keeps processes that are not hosting a foreground ("user visible") app component in a least-recently used (LRU) cache. For example, when the user first launches an app, a process is created for it, but when the user leaves the app, that process does notquit. The system keeps the process cached, so if the user later returns to the app, the process is reused for faster app switching.
If your app has a cached process and it retains memory that it currently does not need, then your app—even while the user is not using it—is constraining the system's overall performance. So, as the system runs low on memory, it may kill processes in the LRU cache beginning with the process least recently used, but also giving some consideration toward which processes are most memory intensive. To keep your process cached as long as possible, follow the advice in the following sections about when to release your references.
More information about how processes are cached while not running in the foreground and how Android decides which ones can be killed is available in the Processes and Threads guide.
-
当用户切换应用的时候,别切换到后端的应用使用的内存并没有被切换或者删除,进程会被缓存到LRU缓存中。比如用户首先启动了一个应用,系统会创建一个进程,当用户离开这个应用,这个进程并没有退出,而是别缓存到LRU缓存中,所以当用户再次返回到这个应用的时候,能更快速更高效的启动,从而应用切换更快。
-
如果你的应用被缓存到LRU中,还在不断的申请内存或不释放占用的(不需要的)内存,这会影响到系统的性能,所以,当系统进行到内存低可用的时候,会从LRU缓存中最早一个被缓存的程序开始移除(释放进程暂用的资源),同时也会考虑应用所暂用的内存的大小。为了能让你的应用尽可能长的在LRU中缓存,可以更加以下建议释放引用的资源。
-
更多关于系统入会缓存应用到LRU已经如何移除应用,参考 Processes and Threads
-
How Your App Should Manage Memory 如何管理你应用的内存
You should consider RAM constraints throughout all phases of development, including during app design (before you begin development). There are many ways you can design and write code that lead to more efficient results, through aggregation of the same techniques applied over and over.
你应该在所有阶段都考虑到内存的使用,包括在应用程序设计期间(在开始开发之前)。有很多方法可以设计和编写高效使用内存的代码,可以通过反复结合这些技术,不断优化应用。You should apply the following techniques while designing and implementing your app to make it more memory efficient.你应该通过应用以下技术设计和实现应用程序,使其高效的使用内存。
Use services sparingly 谨慎保守的使用service
If your app needs a service to perform work in the background, do not keep it running unless it's actively performing a job. Also be careful to never leak your service by failing to stop it when its work is done.如果你有应用在后台运行,那么请在任务完成后第一时间关闭,而且要注意不要因为发生错误而导致无法关闭service而引起内存泄露。
When you start a service, the system prefers to always keep the process for that service running. This makes the process very expensive because the RAM used by the service can’t be used by anything else or paged out. This reduces the number of cached processes that the system can keep in the LRU cache, making app switching less efficient. It can even lead to thrashing(
通常是因为内存或其他资源耗尽或有限而无法完成所要执行的操作,会严重影响系统性能
) in the system when memory is tight and the system can’t maintain enough processes to host all the services currently running.The best way to limit the lifespan of your service is to use an
IntentService
, which finishes itself as soon as it's done handling the intent that started it. For more information, read Running in a Background Service .当你启动一个service的时候,系统会优先保持这个service进程的运行,这个代价很昂贵因为service使用的内存是不能被别人使用或者通过页交换出去。这就直接导致LRU缓存的大小,进而影响到应用间的切换效率,更甚至导致抖动通常是因为内存或其他资源耗尽或有限而无法完成所要执行的操作,会严重影响系统性能。最好的解决办法就是使用IntentService来限制service的寿命。IntentService会在自身任务完成后在第一时间释放掉所占用内存与所引用的intent。详见Running in a Background ServiceLeaving a service running when it’s not needed is one of the worst memory-management mistakes an Android app can make. So don’t be greedy by keeping a service for your app running. Not only will it increase the risk of your app performing poorly due to RAM constraints, but users will discover such misbehaving apps and uninstall them.让一个service在不需要的时候运行在后台是android应用犯得最大的内存使用错误。所以不要贪心的通过让一个service不断在后台运行从而让自己的应用持续运行。这不光是增加了自己应用内存不足甚至溢出的风险,而且这种不友好的行为,用户会发现并卸载了你的应用。
-
Release memory when your user interface becomes hidden 当你的应用被切换到后台时及时释放内存
When the user navigates to a different app and your UI is no longer visible, you should release any resources that are used by only your UI. Releasing UI resources at this time can significantly increase the system's capacity for cached processes, which has a direct impact on the quality of the user experience.当你跳转到其他的应用,你应用的页面UI不在被用户可见,你应该释放掉任何只有你应用使用的UI资源。释放掉这些UI资源能明显有效的减少缓存你应用到LRU的容量。这能直接提高用户体验。
To be notified when the user exits your UI, implement the
onTrimMemory()
callback in yourActivity
classes. You should use this method to listen for theTRIM_MEMORY_UI_HIDDEN
level, which indicates your UI is now hidden from view and you should free resources that only your UI uses.为了做到适时释放UI,应该在activity中实现ComponentCallbacks2接口(API 14)的
onTrimMemory()
回调并监听TRIM_MEMORY_UI_HIDDEN
级别。这表明UI现在被隐藏了应该要释放只有你UI使用的资源了。Notice that your app receives the
onTrimMemory()
callback withTRIM_MEMORY_UI_HIDDEN
only when all the UI components of your app process become hidden from the user. This is distinct from theonStop()
callback, which is called when anActivity
instance becomes hidden, which occurs even when the user moves to another activity in your app. So although you should implementonStop()
to release activity resources such as a network connection or to unregister broadcast receivers, you usually should not release your UI resources until you receiveonTrimMemory(TRIM_MEMORY_UI_HIDDEN)
. This ensures that if the user navigates back from another activity in your app, your UI resources are still available to resume the activity quickly.
-
注意当你的UI只有部分被遮挡的时候,onStop方法会调用但是onTrimMemory(TRIM_MEMORY_UI_HIDDEN)不会被调用。所以不要在onsTrop方法中释放UI资源,以便用户返回时能快速显示。
-
Release memory as memory becomes tight
During any stage of your app's lifecycle, the
onTrimMemory()
callback also tells you when the overall device memory is getting low. You should respond by further releasing resources based on the following memory levels delivered byonTrimMemory()
:TRIM_MEMORY_RUNNING_MODERATE 内存运行平稳
Your app is running and not considered killable, but the device is running low on memory and the system is actively killing processes in the LRU cache.
-
你的应用不会被关掉,但是设备运行在低内存环境,系统会关闭在LRU缓存中的进程。
TRIM_MEMORY_RUNNING_LOW 内存低
Your app is running and not considered killable, but the device is running much lower on memory so you should release unused resources to improve system performance (which directly impacts your app's performance).
-
你的应用不会被关掉,但是设备运转在非常低的内存环境中,你需要主动释放一部分不必要的资源,因为这个时候的系统运转很慢,直接影响了你程序的性能。
TRIM_MEMORY_RUNNING_CRITICAL
Your app is still running, but the system has already killed most of the processes in the LRU cache, so you should release all non-critical resources now. If the system cannot reclaim sufficient amounts of RAM, it will clear all of the LRU cache and begin killing processes that the system prefers to keep alive, such as those hosting a running service.
-
你的应用还是会运行,但是系统已经关闭了LRU缓存中大部分的进程,所以你现在应该释放掉所有的非关键的资源了。如果系统不能继续申请到内存,它就会清空LRU缓存,开始关闭进程已确保系统自身的运行,比如那些持有 处于运行中的service的进程。
Also, when your app process is currently cached, you may receive one of the following levels from
onTrimMemory()
:同样当你的APP进程正被缓存,你也许会在onTrimMemory中收到如下级别的提示。TRIM_MEMORY_BACKGROUND 应用被放到LRU
The system is running low on memory and your process is near the beginning of the LRU list. Although your app process is not at a high risk of being killed, the system may already be killing processes in the LRU cache. You should release resources that are easy to recover so your process will remain in the list and resume quickly when the user returns to your app.
-
系统运行低内存并且你的应用在LRU缓存表的开始,尽管你的应该没有被关闭的高风险,系统也许已经开始在LRU缓存中关闭进程,你也应该要释放掉容易恢复的资源,这样你的进程就能继续保持在LRU缓存列表并且可以快速恢复现场当用户返回你的应用时。
TRIM_MEMORY_MODERATE
The system is running low on memory and your process is near the middle of the LRU list. If the system becomes further constrained for memory, there's a chance your process will be killed.
-
系统运行低内存并且你的应用在LRU缓存表的中间位置,如果你的系统内存吃紧更进一步,你的应用就有可能被关掉。
TRIM_MEMORY_COMPLETE 内存完全不够用
The system is running low on memory and your process is one of the first to be killed if the system does not recover memory now. You should release everything that's not critical to resuming your app state.
-
系统运行低内存并且你的应用被系统首先关闭的应用之一,如果系统现在内存还是不够,你应该释放掉所有与恢复你应用状态无关的资源(比如你可以记录用户输入的数据,而释放掉输入框使用的图片资源等)。
Because the
onTrimMemory()
callback was added in API level 14, you can use theonLowMemory()
callback as a fallback for older versions, which is roughly equivalent to theTRIM_MEMORY_COMPLETE
event.因为onTrimMemory是在API14以后才支持,那么老版本可以用onLowMemory方法代替,但是这个方法只是onTrimmeory方法中TrIM_MEMORY_COMPLETE这一种情况。Note: When the system begins killing processes in the LRU cache, although it primarily works bottom-up, it does give some consideration to which processes are consuming more memory and will thus provide the system more memory gain if killed. So the less memory you consume while in the LRU list overall, the better your chances are to remain in the list and be able to quickly resume.
注意:当系统需要关闭LRU缓存中进程的时候,虽然是首先考虑从LRU缓存列表下到上的顺序(最先别缓存的应用最先被删除),但是系统同时也会考虑是否关闭哪些占用内存特别多的内存。所以你的应用占用的内存越少,那么在LRU缓存中时间就会越长,下次用户回到你应用的时候,便能迅速恢复现场。 -
未完待续
-
-
-
-
-