移动系统对资源的限制和要求
移动操作系统现对于PC端的一个首要特点就是资源有限,比如内存、电池、网络的不确定等等,这些资源相对于PC端来说都很有限,当然对于发展到今天的移动端设备来说,这个已经不算是一个大问题了,不过,在移动端开发中,针对资源有限的问题都是一个不得不正视的问题。本文主要针对内存这一块进行阐述,针对移动网络的特点、电池优化的问题、有后续系列阐述文章。
移动操作系统的几个主要特点
- 移动设备需要便携,所以需要携带电池,需要专门的电池管理,这一点,笔者当年首次学习Symbian系统时却有体会;
- 移动设备通常情况下,屏幕比较小,但是需要展示的信息一点也不少,所以,就需要在界面设计以及交互设计上(UI UE)需要更多的想象力,需要更好的更人性化的设计,需要面对的情况也更复杂;
- 由于移动设备的大部分都需要电池供电,所以,在选择CPU时,也就必须考虑好CPU的功耗问题,复杂指令集的基本不建议在考虑之列。
- 同样由于电池所限,使用的内存(memory)和存储相对于PC桌面系统来说都相当受限制。
- 由于移动设备资源所限,大部分移动设备操作系统,都需要分时复用来管理所有的需要运行的程序(通常叫App)。
- 现阶段流行的移动操作系统主要是ios和android,其中大部分程序都运行在后台情况下,都由系统层面管理App所占用的各类资源,比如内存,当程序后台运行时,大部分情况下内存都会被回收。
- 移动操作系统一般情况下都会限制每个应用所消耗的资源总量。
- 作为App开发者,管理好自身App所消耗的资源是一个应尽的义务。
- 管理
Android内存管理机制浅析
Android是2007年Google在收购基础上推出的基于Linux操作系统的开源移动操作系统,该平台由操作系统、中间件、用户界面和应用软件组成。
Android结构
从架构图看,Android分为四个层,分别是应用程序层、应用程序框架层、系统运行库层和Linux层。
Android应用开发
在Android平台上开发应用程序,包括java 和C++(for Native)。
android平台的内存管理
一般情况下(多进程例外)每个应用都会由系统分配一个进程(zygote)资源。这段Android代码可以获取当前进程的内存情况。
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memClass = activityManager.getMemoryClass();
int maxClass = activityManager.getLargeMemoryClass();
任何一个进程最多可以获取的内存资源时有限的。
内存优化思路
强引用与弱引用
- 在一个Activity生命周期中,其中所有定义的变量的生命周期都和Activty保持一致,如果用强引用的话,那么变量所指向的内存和Activity生命周期保持一致,但是在很多情况下,从逻辑上这个成员变量已经不需要了,由于强引用其所指向的内存区域也保持一样的什么周期,有点得不偿失。
- 这种情况下,可以选择弱引用,所谓弱引用就是指其指向的内存区域,在内存比较紧张的时候可以被GC。这样就可以减少内存的使用。
WeakReference<ForeEngine> mWeakForeEngine;
ForeEngine mForeEngine;
其中:
- mForeEngine是强引用,mWeakForeEngine是弱引用
- mWeakForeEngine指向的内存在mWeakForeEngine运行过程中可能会被回收
弱引用的初始化
mWeakForeEngine = new WeakReference<ForeEngine>(foreEngine) ;
弱引用的使用
ForeEngine foreEngine = mWeakForeEngine.get();
if(foreEngine == null) {
// todo
}
关注内存大户Bitmap
待续
Android内存管理原理简析
Java虚拟机模型
JVM内存模型,java虚拟机在构建RunTime运行时数据内存分配,
内存主要分为方法区,虚拟机栈,本地方法栈,堆,PCR程序计数器。
JVM内存模型示意
Android的改进
- 寄存器和栈的区别
* JVM虚拟机基于栈,DVM基于寄存器。*
- Dalvik和java虚拟的区别 dex class的区别
- dalvik art的改进
android虚拟机都使用页式和memory mapping方式进行内存管理,虚拟机分配并决定了内存的生命周期,并且使用GC(*garbage collection*)来回收已分配但是不再使用的内存片段,GC的主要工作主要分为两个部分:
1)找到内存中不再使用的数据片段
2)回收这些资源
分配内存资源主要是分代管理,刚刚分配的内存区域叫Young Generation。
对象在Young Generation时间比较长之后,就会被划到Older Generation,还有一个permanent generation。
尽管内存回收的速度较快,但是由于会在程序运行过程中的停下执行GC,不可避免的的会影响程序的运行,一旦GC边界条件被触发,系统就会停止当前进程,开始GC。
关于GC的详细机制详见,ref:
- Android Runtime(ART)的进一步改进措施
1) ART中暂停次数相比于Dalvik有减少,从两次减为一次;
2)ART GC 一样有暂停中断,不一样之处在于,ART在有些阶段比如引用过程,sytem sweeping过程,等阶段中可以并发的执行GC。
DVM算法简析
- 程序运行过程中,不断申请新的对象消耗内存,直到用完所有,然后创建新的对象需要内存的时候,暂停运行,出发GC 回收,器原理就是从GC Roots开始,将整个内存遍历,保留所有被直接以及间接用的内存区域,余下的被回收。
该算法可以解决内存的问题,释放内存。 - 但是缺点一样存在,1 从GC Roots开始的遍历是一个递归调用,这个过程本身会消耗很多资源,一方面在内存不多时候消耗内存递归,另一方面,如果遍历非常深的话,消耗的时间资源也很明显。所有后来的android系统优化了这个过程,另开启线程逐步释放内存,尽量不影响程序的正常运行。也就是逐步GC,还有一个叫CMS(concurrent mark sweep)。
-
下面是代码
1 启动VM
\dalvik\vm\Init.cpp
/*
* VM initialization. Pass in any options provided on the command line.
* Do not pass in the class name or the options for the class.
*
* Returns 0 on success.
*/
std::string dvmStartup(int argc, const char* const argv[],
bool ignoreUnrecognized, JNIEnv* pEnv)
其中 启动的内容很多,有兴趣的可以参考源代码。
2 GC 启动
在main heap上采用mmap管理内存。
dalvik\vm\alloc\alloc.cpp
/*
* Initialize the GC universe.
*
* We're currently using a memory-mapped arena to keep things off of the
* main heap. This needs to be replaced with something real.
*/
bool dvmGcStartup()
{
dvmInitMutex(&gDvm.gcHeapLock);
pthread_cond_init(&gDvm.gcHeapCond, NULL);
return dvmHeapStartup();
}
3 GC heap启动
dalvik\vm\alloc\Heap.cpp
初始化GC heap 并建立VM card table。
/*
* Initialize the GC heap.
*
* Returns true if successful, false otherwise.
*/
bool dvmHeapStartup()
{
GcHeap *gcHeap;
if (gDvm.heapGrowthLimit == 0) {
gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
}
gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
gDvm.heapMaximumSize,
gDvm.heapGrowthLimit);
if (gcHeap == NULL) {
return false;
}
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
gDvm.gcHeap = gcHeap;
/* Set up the lists we'll use for cleared reference objects.
*/
gcHeap->clearedReferences = NULL;
if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
LOGE_HEAP("card table startup failed.");
return false;
}
return true;
}
4
dalvik\vm\alloc\HeapSource.cpp
为了不和zygote heap的内存发生交集,在首次fork之前调用它,这个仍然会有一些造成小片段内存的问题存在
/*
* This is called while in zygote mode, right before we fork() for the
* first time. We create a heap for all future zygote process allocations,
* in an attempt to avoid touching pages in the zygote heap. (This would
* probably be unnecessary if we had a compacting GC -- the source of our
* troubles is small allocations filling in the gaps from larger ones.)
*/
bool dvmHeapSourceStartupBeforeFork()
{
HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
HS_BOILERPLATE();
assert(gDvm.zygote);
if (!gDvm.newZygoteHeapAllocated) {
/* Ensure heaps are trimmed to minimize footprint pre-fork.
*/
trimHeaps();
/* Create a new heap for post-fork zygote allocations. We only
* try once, even if it fails.
*/
ALOGV("Splitting out new zygote heap");
gDvm.newZygoteHeapAllocated = true;
return addNewHeap(hs);
}
return true;
}