Android应用创建在Java虚拟机之上的,Google为了保证同时多个APP运行并及时唤醒,就为每一个虚拟机设置了最大可以使用内存,经过adb命令能够查看相应的几个参数,java
* [dalvik.vm.heapgrowthlimit]: [192m]
* [dalvik.vm.heapmaxfree]: [8m]
* [dalvik.vm.heapminfree]: [512k]
* [dalvik.vm.heapsize]: [512m]
* [dalvik.vm.heapstartsize]: [8m]
* [dalvik.vm.heaptargetutilization]: [0.75]复制代码
其中dalvik.vm.heapsize是最大可使用的内存,这个数值同厂商跟版本都有关系,随着配置的提升,都在逐渐增大,既然虚拟机能使用的最大内存是dalvik.vm.heapsize,那么在申请内存的时候是否是一直到最大值才会GC呢?答案确定是否认的,从咱们检测的曲线来看,在内存使用很低的时候,也会GC,看下图APP运行时状况:android
从上图看到,1,2,3这三个点好像是都发生了GC,可是这个时候,APP内存的占用并非很高,距离最大内存还有很远,那么这个时候为何会发生内存GC呢,其实直观上也比较好理解,若是一直等到最大内存才GC,那么就会有两个弊端:首先,内存资源浪费,形成系统性能下降,其次,GC时内存占用越大,耗时越长,应尽可能避免。那GC的时机究竟是何时呢?是否是每次内存块分配的时候都会GC,这个应该也是否认的,本文就来简单的了解下内存分配、GC、内存增加等机制。bash
首先看一下虚拟机的配置参数的意义,上面只讲述了dalvik.vm.heapstartsize,是最大内存申请尺寸,app
后面三个值用来确保每次GC以后Java堆已经使用和空闲的内存有一个合适的比例,这样能够尽可能地减小GC的次数,堆的利用率为U,最小空闲值为MinFree字节,最大空闲值为MaxFree字节,假设在某一次GC以后,存活对象占用内存的大小为LiveSize。那么这时候堆的理想大小应该为(LiveSize / U)。可是(LiveSize / U)必须大于等于(LiveSize + MinFree)而且小于等于(LiveSize + MaxFree),不然,就要进行调整,调整的实际上是软上限softLimit,性能
static size_t getUtilizationTarget(const HeapSource* hs, size_t liveSize)
{
size_t targetSize = (liveSize / hs->targetUtilization) * HEAP_UTILIZATION_MAX;
if (targetSize > liveSize + hs->maxFree) {
targetSize = liveSize + hs->maxFree;
} else if (targetSize < liveSize + hs->minFree) {
targetSize = liveSize + hs->minFree;
}
return targetSize;
}复制代码
以上就是计算公式的源码,假设liveSize = 150M,targetUtilization=0.75,maxFree=8,minFree=512k,那么理想尺寸200M,而200M很明显超过了150+8,那么这个时候,堆的尺寸就应该调整到158M,这个softLimit软上限也是下次申请内存时候是否须要GC的一个重要指标,请看如下场景:ui
场景一:当前softLimit=158M,liveSize = 150M,若是这个时候,须要分配一个100K内存的对象spa
因为当前的上限是158M,内存是能够直接分配成功的,分配以后,因为空闲内存8-100K>512k,也不须要调整内存,这个时候,不存在GC,3d
场景二:当前softLimit=158M,liveSize = 150M,若是这个时候,须要分配的内存是7.7Mcode
因为当前的上限是158M,内存是能够直接分配成功的,分配以后,因为空闲内存8-7.7M < 512k,那就须要GC,同时调整softLimitcdn
场景三:当前softLimit=158M,liveSize = 150M,若是这个时候,须要分配的内存是10M
因为当前的上限是158M,内存分配失败,须要先GC,GC以后调整softLimit,再次请求分配,若是仍是失败,将softLimit调整为最大,再次请求分配,失败就再GC一次软引用,再次请求,仍是失败那就是OOM,成功后要调整softLimit
因此,Android在申请内存的时候,可能先分配,也可能先GC,也可能不GC,这里面最关键的点就是内存利用率跟Free内存的上下限,下面简单看源码了解下堆内存分配流程:
static void *tryMalloc(size_t size)
{
void *ptr;
<!--1 首次请求分配内存-->
ptr = dvmHeapSourceAlloc(size);
if (ptr != NULL) {
return ptr;
}
<!--2 分配失败,GC-->
if (gDvm.gcHeap->gcRunning) {
dvmWaitForConcurrentGcToComplete();
} else {
gcForMalloc(false);
}
<!--再次分配-->
ptr = dvmHeapSourceAlloc(size);
if (ptr != NULL) {
return ptr;
}
<!--仍是分配失败,调整softLimit再次分配-->
ptr = dvmHeapSourceAllocAndGrow(size);
if (ptr != NULL) {
size_t newHeapSize;
<!--分配成功后要调整softLimit-->
newHeapSize = dvmHeapSourceGetIdealFootprint();
return ptr;
}
<!--仍是分配失败,GC力增强,回收soft引用,-->
gcForMalloc(true);
<!--再次请求分配,若是仍是失败,那就OOM了-->
ptr = dvmHeapSourceAllocAndGrow(size);
if (ptr != NULL) {
return ptr;
}
dvmDumpThread(dvmThreadSelf(), false); return NULL;
}复制代码
本文主要说的一个问题就是,为何不等到最大内存在GC,以及普通GC的可能时机,固然,对于内存的GC是更加复杂的,不在本文的讨论范围以内,同时这个也解释频繁的分配大内存会致使GC抖动的缘由,毕竟,若是你超过了maxFree ,就必定GC,有兴趣能够自行深刻分析。
做者:看书的小蜗牛
原文连接:Android内存分配/回收的一个问题-为何低内存的时候也GC
仅供参考,欢迎指正