上一节介绍了dumpsys meminfo命令执行的大致流程,这一节要讲的是dumpsys meminfo如何从native层拿到原始的memory系统数据以及dumpsys meminfo各数据的组成。
我们知道Java要与native之间进行相互调用,必须通过JNI层,在dumpsys meminfo执行过程中需要从native拿到从kernel传上来的系统memory的相关数据,这就需要通过JNI层。
具体过程如下:
首先是两个从native拿数据,位于debug.java中的函数:
public static native void getMemoryInfo(MemoryInfo memoryInfo);
public static native void getMemInfo(long[] outSizes);
先看getMemoryInfo()函数,此函数是调用的native的函数,经过jni转化:
{ "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
(void*) android_os_Debug_getDirtyPagesPid },
调用native的函数android_os_Debug_getDirtyPagesPid():
static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)//更新Java层pss相关的信息
{
bool foundSwapPss = false;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
load_maps(pid, stats, &foundSwapPss);//对当前进程的smaps的信息进行统计,统计完成之后放到stats[]数组中
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {//获取graphic的memory消耗数据,graphic所占用的数据必须单独统计
stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
stats[HEAP_GRAPHICS].privateDirty = graphics_mem.graphics;
stats[HEAP_GL].pss = graphics_mem.gl;
stats[HEAP_GL].privateDirty = graphics_mem.gl;
stats[HEAP_OTHER_MEMTRACK].pss = graphics_mem.other;
stats[HEAP_OTHER_MEMTRACK].privateDirty = graphics_mem.other;
}
for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {//将除native和dalvik所占用的内存除外的内存占用类型全部划为other_***
stats[HEAP_UNKNOWN].pss += stats[i].pss;
stats[HEAP_UNKNOWN].swappablePss += stats[i].swappablePss;
stats[HEAP_UNKNOWN].privateDirty += stats[i].privateDirty;
stats[HEAP_UNKNOWN].sharedDirty += stats[i].sharedDirty;
stats[HEAP_UNKNOWN].privateClean += stats[i].privateClean;
stats[HEAP_UNKNOWN].sharedClean += stats[i].sharedClean;
stats[HEAP_UNKNOWN].swappedOut += stats[i].swappedOut;
stats[HEAP_UNKNOWN].swappedOutPss += stats[i].swappedOutPss;
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {//将other_**, native_***和dalvik_***全部放到java层的meminfo类中对应的变量中,具体如何操作将在后面讲到
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
env->SetIntField(object, stat_fields[i].pssSwappable_field, stats[i].swappablePss);
env->SetIntField(object, stat_fields[i].privateDirty_field, stats[i].privateDirty);
env->SetIntField(object, stat_fields[i].sharedDirty_field, stats[i].sharedDirty);
env->SetIntField(object, stat_fields[i].privateClean_field, stats[i].privateClean);
env->SetIntField(object, stat_fields[i].sharedClean_field, stats[i].sharedClean);
env->SetIntField(object, stat_fields[i].swappedOut_field, stats[i].swappedOut);
env->SetIntField(object, stat_fields[i].swappedOutPss_field, stats[i].swappedOutPss);
}
env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss == false ? JNI_FALSE : JNI_TRUE);
jintArray otherIntArray = (jintArray)env->GetObjectField(object, otherStats_field);
jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
if (otherArray == NULL) {
return;
}
int j=0;
for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {//将之前统计在other_***类型内存中的数据进行整理,存放在Java的meminfo类的otherstat[]数组当中
otherArray[j++] = stats[i].pss;
otherArray[j++] = stats[i].swappablePss;
otherArray[j++] = stats[i].privateDirty;
otherArray[j++] = stats[i].sharedDirty;
otherArray[j++] = stats[i].privateClean;
otherArray[j++] = stats[i].sharedClean;
otherArray[j++] = stats[i].swappedOut;
otherArray[j++] = stats[i].swappedOutPss;
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
}
android_os_Debug_getDirtyPagesPid()调用的是load_maps()函数:
static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
char tmp[128];
FILE *fp;
sprintf(tmp, "/proc/%d/smaps", pid);
fp = fopen(tmp, "r");//打开proc/<pid>/smaps文件
if (fp == 0) return;
read_mapinfo(fp, stats, foundSwapPss);//解析smaps文件
fclose(fp);
}
接着调用的是read_mapinfo():
static void read_mapinfo(FILE *fp, stats_t* stats, bool* foundSwapPss)
{
char line[1024];
int len, nameLen;
bool skip, done = false;
unsigned pss = 0, swappable_pss = 0;
float sharing_proportion = 0.0;
unsigned shared_clean = 0, shared_dirty = 0;
unsigned private_clean = 0, private_dirty = 0;
unsigned swapped_out = 0, swapped_out_pss = 0;
bool is_swappable = false;
unsigned temp;
uint64_t start;
uint64_t end = 0;
uint64_t prevEnd = 0;
char* name;
int name_pos;
int whichHeap = HEAP_UNKNOWN;
int subHeap = HEAP_UNKNOWN;
int prevHeap = HEAP_UNKNOWN;
*foundSwapPss = false;
if(fgets(line, sizeof(line), fp) == 0) return;
while (!done) {//循环解析smaps每一行的内容,将这些所占用的内存进行分类,然后将所得到的各类的内存值再根据不同的pss进行累加,最后的结果存放在stats[]数组里面
prevHeap = whichHeap;
prevEnd = end;
whichHeap = HEAP_UNKNOWN;
subHeap = HEAP_UNKNOWN;
skip = false;
is_swappable = false;
len = strlen(line);
if (len < 1) return;
line[--len] = 0;
if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
skip = true;
} else {
while (isspace(line[name_pos])) {
name_pos += 1;
}