几种内存信息获取方式源码分析

这里简单总结下Android获取内存的方式,方式仅限于系统开放给应用层的API,adb命令比较简单,不在本题讨论范围内,想了解的可以参考之前文章:性能优化工具(十)- Android内存分析命令

一、AMS获取内存信息

1.1 获取方式

ActivityManager am = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);

memoryInfo.totalMem//系统总内存
memoryInfo.availMem//可使用内存

1.2 源码分析
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
    final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
   final long cachedAppMem = mProcessList.getMemLevel(ProcessList.CACHED_APP_MIN_ADJ);
   outInfo.availMem = getFreeMemory();
   outInfo.totalMem = getTotalMemory();
   outInfo.threshold = homeAppMem;
   outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((cachedAppMem-homeAppMem)/2));
   outInfo.hiddenAppThreshold = cachedAppMem;
   outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
            ProcessList.SERVICE_ADJ);
   outInfo.visibleAppThreshold = mProcessList.getMemLevel(
            ProcessList.VISIBLE_APP_ADJ);
   outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
            ProcessList.FOREGROUND_APP_ADJ);
}

getFreeMemory与getTotalMemory都是android.os.Process的方法:

android/os/Process.java

public static final native long getFreeMemory();
public static final native long getTotalMemory();

native方法具体实现:

frameworks/base/core/jni/android_util_Process.cpp

static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
    static const char* const sums[] = { "MemFree:", "Cached:", NULL };
    static const size_t sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), 0 };
    return getFreeMemoryImpl(sums, sumsLen, 2);
}

static jlong android_os_Process_getTotalMemory(JNIEnv* env, jobject clazz)
{
    static const char* const sums[] = { "MemTotal:", NULL };
    static const size_t sumsLen[] = { strlen("MemTotal:"), 0 };
    return getFreeMemoryImpl(sums, sumsLen, 1);
}

static jlong getFreeMemoryImpl(const char* const sums[], const size_t sumsLen[], size_t num)
{
    int fd = open("/proc/meminfo", O_RDONLY);
    if (fd < 0) {
        ALOGW("Unable to open /proc/meminfo");
        return -1;
    }
    char buffer[256];
    const int len = read(fd, buffer, sizeof(buffer)-1);
    close(fd);
    if (len < 0) {
        ALOGW("Unable to read /proc/meminfo");
        return -1;
    }
    buffer[len] = 0;
    size_t numFound = 0;
    jlong mem = 0;
    char* p = buffer;
    while (*p && numFound < num) {
        int i = 0;
        while (sums[i]) {
            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
                p += sumsLen[i];
                while (*p == ' ') p++;
                char* num = p;
                while (*p >= '0' && *p <= '9') p++;
                if (*p != 0) {
                    *p = 0;
                    p++;
                    if (*p == 0) p--;
                }
                mem += atoll(num) * 1024;
                numFound++;
                break;
            }
            i++;
        }
        p++;
    }
    return numFound > 0 ? mem : -1;
}

该方式是从/proc/meminfo文件读取内存信息:

adb shell cat proc/meminfo

MemTotal:        3721316 kB
MemFree:          132652 kB
MemAvailable:    1250484 kB
Buffers:           87284 kB
Cached:          1205132 kB
…
1.3 总结

memoryInfo.totalMem = MemTotal
memoryInfo.availMem = Cached+MemFree

二、Runtime获取内存信息

2.1 获取方式

Runtime runtime = Runtime.getRuntime();
runtime.maxMemory()
runtime.totalMemory()
runtime.freeMemory()

2.2源码分析

libcore/ojluni/src/main/java/java/lang/Runtime.java

@FastNative
public native long freeMemory();

@FastNative
public native long totalMemory();

@FastNative
    public native long maxMemory();

看看具体实现:

libcore/ojluni/src/main/native/Runtime.c

JNIEXPORT jlong JNICALL
Runtime_freeMemory(JNIEnv *env, jobject this)
{
    return JVM_FreeMemory();
}

JNIEXPORT jlong JNICALL
Runtime_totalMemory(JNIEnv *env, jobject this)
{
    return JVM_TotalMemory();
}

JNIEXPORT jlong JNICALL
Runtime_maxMemory(JNIEnv *env, jobject this)
{
    return JVM_MaxMemory();
}
art/runtime/openjdkjvm/OpenjdkJvm.cc

JNIEXPORT jlong JVM_FreeMemory(void) {
  return art::Runtime::Current()->GetHeap()->GetFreeMemory();
}

JNIEXPORT jlong JVM_TotalMemory(void) {
  return art::Runtime::Current()->GetHeap()->GetTotalMemory();
}

JNIEXPORT jlong JVM_MaxMemory(void) {
  return art::Runtime::Current()->GetHeap()->GetMaxMemory();
}

以GetTotalMemory为例,层层来找:

art/runtime/runtime.h

namespace art {
  class Runtime {
...
      static Runtime* Current() { //runtime实例
         return instance_;
      }
 …
     gc::Heap* GetHeap() const {
         return heap_;
      }
  }
}

这里执行的是Heap的GetTotalMemory方法,找下Heap

art/runtime/gc/heap.h

// When the number of bytes allocated exceeds the footprint TryAllocate returns null indicating
// a GC should be triggered.
size_t max_allowed_footprint_;

// Returns the number of bytes currently allocated.
size_t GetBytesAllocated() const {
  return num_bytes_allocated_.LoadSequentiallyConsistent();
}
art/runtime/gc/heap.cc

size_t Heap::GetTotalMemory() const {
  //max_allowed_footprint_:当前阶段最大已申请的内存大小
 //GetBytesAllocated():当前已分配内存大小
  return std::max(max_allowed_footprint_, GetBytesAllocated());
}

totalMemory也就是指当前虚拟机进程已经向系统申请的内存大小。


art/runtime/gc/heap.h

// Returns the number of bytes currently allocated.
  size_t GetBytesAllocated() const {
    return num_bytes_allocated_.LoadSequentiallyConsistent();//当前分配内存大小
  }

// Implements java.lang.Runtime.maxMemory, returning the maximum amount of memory a program can
  // consume. For a regular VM this would relate to the -Xmx option and would return -1 if no Xmx
  // were specified. Android apps start with a growth limit (small heap size) which is
  // cleared/extended for large apps.
  size_t GetMaxMemory() const {
    // There is some race conditions in the allocation code that can cause bytes allocated to
    // become larger than growth_limit_ in rare cases.
    //当前已分配内存 与 增长上限的最大值。其实也就是虚拟机最大可向系统申请到的内存大小
    return std::max(GetBytesAllocated(), growth_limit_);
  }

// Returns how much free memory we have until we need to grow the heap to perform an allocation.
  // Similar to GetFreeMemoryUntilGC. Implements java.lang.Runtime.freeMemory.
  size_t GetFreeMemory() const {
    size_t byte_allocated = num_bytes_allocated_.LoadSequentiallyConsistent();
    size_t total_memory = GetTotalMemory();
    // Make sure we don't get a negative number.
    //freeMemory = totalMemory - 已分配的内存
    return total_memory - std::min(total_memory, byte_allocated);
  }
2.3 总结

runtime.maxMemory()//获取虚拟机最大分配内存
runtime.totalMemory()//获取虚拟机当前申请到的内存大小
runtime.freeMemory()//获取当前申请内存的剩余内存大小

距离oom还剩余的可分配内存空间:
runtime.maxMemory() - (runtime.totalMemory() - runtime.freeMemory())

三、Debug获取内存信息

3.1获取方式

Debug.MemoryInfo mi = new Debug.MemoryInfo();
Debug.getMemoryInfo(mi);

mi.getTotalPss()
mi.getMemoryStat("summary.java-heap")
mi.dalvikPss
mi.nativePss

Debug.getNativeHeapSize()
Debug.getNativeHeapFreeSize()

Debug获取内存的api比较多。

3.2源码分析
android/os/Debug.java

/**
* Retrieves information about this processes memory usages. This information is broken down by
* how much is in use by dalvik, the native heap, and everything else.
*
* <p><b>Note:</b> this method directly retrieves memory information for the given process
* from low-level data available to it.  It may not be able to retrieve information about
* some protected allocations, such as graphics.  If you want to be sure you can see
* all information about allocations by the process, use
* {@link android.app.ActivityManager#getProcessMemoryInfo(int[])} instead.</p>
*/
public static native void getMemoryInfo(MemoryInfo memoryInfo);

/**
* Returns the size of the native heap.
* @return The size of the native heap in bytes.
*/
public static native long getNativeHeapSize();

/**
* Returns the amount of allocated memory in the native heap.
* @return The allocated size in bytes.
*/
public static native long getNativeHeapAllocatedSize();

/**
* Returns the amount of free memory in the native heap.
* @return The freed size in bytes.
*/
public static native long getNativeHeapFreeSize();
frameworks/base/core/jni/android_os_Debug.cpp

static const JNINativeMethod gMethods[] = {
    { "getNativeHeapSize",      "()J",
            (void*) android_os_Debug_getNativeHeapSize },
    { "getNativeHeapAllocatedSize", "()J",
            (void*) android_os_Debug_getNativeHeapAllocatedSize },
    { "getNativeHeapFreeSize",  "()J",
            (void*) android_os_Debug_getNativeHeapFreeSize },
    { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
            (void*) android_os_Debug_getDirtyPages },
...
};

找到jni对应函数,先看getMemoryInfo

static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
{
    android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
}

static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
        jint pid, jobject object)
{
    bool foundSwapPss;
    stats_t stats[_NUM_HEAP];
    memset(&stats, 0, sizeof(stats));
    load_maps(pid, stats, &foundSwapPss);
    struct graphics_memory_pss graphics_mem;
   if (read_memtrack_memory(pid, &graphics_mem) == 0) {
       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++) {
        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++) {
        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);
    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++) {
        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);
}

先通过load_maps打开/proc/PID/smaps虚拟文件,读取内部信息,root手机可以通过adb shell cat/proce/PID/smaps查看,它输出的信息实际上是应用的用户空间的内存分配表,记录了应用分配的每一块内存的地址,类别,大小等信息,而load_maps方法调用read_mapinfo方法从这个表里面读出每一块内存的分配信息,分类进行累加,得出Native Heap,DalvikHeap等各个类别的内存占用。

再看看几个获取native内存的方法:

static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
{
    struct mallinfo info = mallinfo();
    return (jlong) info.usmblks;
}

static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
{
    struct mallinfo info = mallinfo();
    return (jlong) info.uordblks;
}

static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
{
    struct mallinfo info = mallinfo();
    return (jlong) info.fordblks;
}

这里统一是通过mallinfo()获取的。

struct mallinfo {
  int arena;            /* non-mmapped space allocated from system */ //当前从系统分配的非mmapped字节总数
  int ordblks;         /* number of free chunks */ //空闲块的数量
  int smblks;          /* number of fastbin blocks */ //fastbin块的数量
  int hblks;             /* number of mmapped regions */ //mmapped区域的当前数量
  int hblkhd;           /* space in mmapped regions */ //在mmapped区域中保存的字节总数
  int usmblks;        /* maximum total allocated space */ //分配的最大总空间。
  int fsmblks;         /* space available in freed fastbin blocks */
  int uordblks;        /* total allocated space */ //当前分配的总空间
  int fordblks;         /* total free space */ //总空闲空间
  int keepcost;       /* top-most, releasable (via malloc_trim) space */ //理想情况下可以释放的最大字节数通过malloc_trim返回系统
};

这里对照mallinfo字段:
android_os_Debug_getNativeHeapSize ------ info.usmblks 分配的最大总空间
android_os_Debug_getNativeHeapAllocatedSize -------- info.uordblks 当前分配的总空间
android_os_Debug_getNativeHeapFreeSize ------- info.fordblks 总空闲空间

3.3 总结:

Debug主要能获取到三大块内容:

adb shell dumpsys meminfo pid

1.以Debug.getNativeHeapSize() 为代表的获取native内存信息,底层主要是通过mallinfo获取的。
2.Debug.getMemoryInfo(mi)执行之后,以mi.getTotalPss()为代表的获取从/proc/PID/smaps统计到的内存信息。
3.Debug.getMemoryInfo(mi)执行之后,以mi.getMemoryStat("summary.java-heap”)为代表的2数据的整合。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,000评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,745评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,561评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,782评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,798评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,394评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,952评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,852评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,409评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,483评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,615评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,303评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,979评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,470评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,571评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,041评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,630评论 2 359

推荐阅读更多精彩内容