Android中每个App默认情况下是运行在一个独立进程中的, 而这个独立进程正是从Zygote孵化出来的VM进程, 也就是说, 也就是说每个Android APP在运行时会启动一个Java虚拟机。
并且系统会给它分配固定的内存空间(手机厂商会根据手机的配置情况来对其进行调整)。
Android是一个多任务系统, 为了保证多任务的运行, Android给每个App可使用的Heap大小设定了一个限定值.
这个值是系统设置的prop值, 保存在System/build.prop
文件中. 一般国内的手机厂商都会做修改, 根据手机配置不同而不同, 可以直接打开查看与修改。
其中和虚拟机内存相关的主要有以下三个:
1 . dalvik.vm.heapstartsize
– App启动后,系统分配给它的Heap初始大小,随着App使用可增加。
2 . dalvik.vm.heapgrowthlimit
– 默认情况下, App可使用的Heap的最大值, 超过这个值就会产生OOM.
3 . dalvik.vm.heapsize
– 如果App的manifest文件中配置了largeHeap属性, 那么App可使用的Heap的最大值为此项设定值。
<application
android:largeHeap="true">
...
</application>
所以对于同一个手机,不开启largeHeap
属性时与多进程时,每个APP的虚拟机分配的内存的上限都是heapgrowthlimit
。
Android在ActivityManager类中提供了API可以运行时获取这些属性值,如下:
//ActivityManager的getMemoryClass()获得内用正常情况下内存的大小,即heapgrowthlimit的值
//ActivityManager的getLargeMemoryClass()可以获得开启largeHeap最大的内存大小,即heapsize的指
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
activityManager.getLargeMemoryClass();
虚拟机分配内存的具体源码可以AOSP的Heap.cpp文件中查看:
/* Try as hard as possible to allocate some memory.
*/
static void *tryMalloc(size_t size)
{
void *ptr;
//TODO: figure out better heuristics
// There will be a lot of churn if someone allocates a bunch of
// big objects in a row, and we hit the frag case each time.
// A full GC for each.
// Maybe we grow the heap in bigger leaps
// Maybe we skip the GC if the size is large and we did one recently
// (number of allocations ago) (watch for thread effects)
// DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
// (or, at least, there are only 0-5 objects swept each time)
ptr = dvmHeapSourceAlloc(size);
if (ptr != NULL) {
return ptr;
}
/*
* The allocation failed. If the GC is running, block until it
* completes and retry.
*/
if (gDvm.gcHeap->gcRunning) {
/*
* The GC is concurrently tracing the heap. Release the heap
* lock, wait for the GC to complete, and retrying allocating.
*/
dvmWaitForConcurrentGcToComplete();
} else {
/*
* Try a foreground GC since a concurrent GC is not currently running.
*/
gcForMalloc(false);
}
ptr = dvmHeapSourceAlloc(size);
if (ptr != NULL) {
return ptr;
}
/* Even that didn't work; this is an exceptional state.
* Try harder, growing the heap if necessary.
*/
ptr = dvmHeapSourceAllocAndGrow(size);
if (ptr != NULL) {
size_t newHeapSize;
newHeapSize = dvmHeapSourceGetIdealFootprint();
//TODO: may want to grow a little bit more so that the amount of free
// space is equal to the old free space + the utilization slop for
// the new allocation.
LOGI_HEAP("Grow heap (frag case) to "
"%zu.%03zuMB for %zu-byte allocation",
FRACTIONAL_MB(newHeapSize), size);
return ptr;
}
/* Most allocations should have succeeded by now, so the heap
* is really full, really fragmented, or the requested size is
* really big. Do another GC, collecting SoftReferences this
* time. The VM spec requires that all SoftReferences have
* been collected and cleared before throwing an OOME.
*/
//TODO: wait for the finalizers from the previous GC to finish
LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation",
size);
gcForMalloc(true);
ptr = dvmHeapSourceAllocAndGrow(size);
if (ptr != NULL) {
return ptr;
}
//TODO: maybe wait for finalizers and try one last time
LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
//TODO: tell the HeapSource to dump its state
dvmDumpThread(dvmThreadSelf(), false);
return NULL;
}
具体流程如下:
所以产生OOM时,一定是java的堆中 已有的内存 + 申请的内存 >= heapgrowthlimit导致的,不会因为手机目前物理内存是否紧张而改变 - 当物理内存非常紧张时系统会通过LowMemory Killer杀掉一些低优先级的进程。
相应的,物理内存非常充足的情况也会有OOM的情况发生。
当APP出现OOM时,建议可以从以下两个方向来处理:
1 . 排查内存泄露问题
排查各个功能是否内存泄露情况,可以通过Android Studio中的MemoryMonitor功能进行分析,Memory Monitor也集成了HPROF Viewer和Allocation Tracker可以分析内存快照与内存分配追踪。另外推荐一个工具,square公司开源的leakcanary,非常简洁好用。
2 . 内存优化
按照谷歌在youtube上发布的性能优化典范之内存篇,优化各功能的内存,或可参照 胡凯的总结 。大致有以下这些,具体请参见原文:
考虑不同的实现方式来优化内存占用
注意Cursor对象的及时关闭
StringBuilder代替String
使用更小的图片
Copyright© 2013-2019