幾種內(nèi)存信息獲取方式源碼分析

這里簡單總結(jié)下Android獲取內(nèi)存的方式,方式僅限于系統(tǒng)開放給應(yīng)用層的API,adb命令比較簡單,不在本題討論范圍內(nèi),想了解的可以參考之前文章:性能優(yōu)化工具(十)- Android內(nèi)存分析命令。

一、AMS獲取內(nèi)存信息

1.1 獲取方式

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

memoryInfo.totalMem//系統(tǒng)總內(nèi)存
memoryInfo.availMem//可使用內(nèi)存

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方法具體實現(xiàn):

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文件讀取內(nèi)存信息:

adb shell cat proc/meminfo

MemTotal:        3721316 kB
MemFree:          132652 kB
MemAvailable:    1250484 kB
Buffers:           87284 kB
Cached:          1205132 kB
…
1.3 總結(jié)

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

二、Runtime獲取內(nèi)存信息

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();

看看具體實現(xiàn):

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_;
      }
  }
}

這里執(zhí)行的是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_:當前階段最大已申請的內(nèi)存大小
 //GetBytesAllocated():當前已分配內(nèi)存大小
  return std::max(max_allowed_footprint_, GetBytesAllocated());
}

totalMemory也就是指當前虛擬機進程已經(jīng)向系統(tǒng)申請的內(nèi)存大小。


art/runtime/gc/heap.h

// Returns the number of bytes currently allocated.
  size_t GetBytesAllocated() const {
    return num_bytes_allocated_.LoadSequentiallyConsistent();//當前分配內(nèi)存大小
  }

// 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.
    //當前已分配內(nèi)存 與 增長上限的最大值。其實也就是虛擬機最大可向系統(tǒng)申請到的內(nèi)存大小
    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 - 已分配的內(nèi)存
    return total_memory - std::min(total_memory, byte_allocated);
  }
2.3 總結(jié)

runtime.maxMemory()//獲取虛擬機最大分配內(nèi)存
runtime.totalMemory()//獲取虛擬機當前申請到的內(nèi)存大小
runtime.freeMemory()//獲取當前申請內(nèi)存的剩余內(nèi)存大小

距離oom還剩余的可分配內(nèi)存空間:
runtime.maxMemory() - (runtime.totalMemory() - runtime.freeMemory())

三、Debug獲取內(nèi)存信息

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獲取內(nèi)存的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 },
...
};

找到j(luò)ni對應(yīng)函數(shù),先看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虛擬文件,讀取內(nèi)部信息,root手機可以通過adb shell cat/proce/PID/smaps查看,它輸出的信息實際上是應(yīng)用的用戶空間的內(nèi)存分配表,記錄了應(yīng)用分配的每一塊內(nèi)存的地址,類別,大小等信息,而load_maps方法調(diào)用read_mapinfo方法從這個表里面讀出每一塊內(nèi)存的分配信息,分類進行累加,得出Native Heap,DalvikHeap等各個類別的內(nèi)存占用。

再看看幾個獲取native內(nèi)存的方法:

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;
}

這里統(tǒng)一是通過mallinfo()獲取的。

struct mallinfo {
  int arena;            /* non-mmapped space allocated from system */ //當前從系統(tǒng)分配的非mmapped字節(jié)總數(shù)
  int ordblks;         /* number of free chunks */ //空閑塊的數(shù)量
  int smblks;          /* number of fastbin blocks */ //fastbin塊的數(shù)量
  int hblks;             /* number of mmapped regions */ //mmapped區(qū)域的當前數(shù)量
  int hblkhd;           /* space in mmapped regions */ //在mmapped區(qū)域中保存的字節(jié)總數(shù)
  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 */ //理想情況下可以釋放的最大字節(jié)數(shù)通過malloc_trim返回系統(tǒng)
};

這里對照mallinfo字段:
android_os_Debug_getNativeHeapSize ------ info.usmblks 分配的最大總空間
android_os_Debug_getNativeHeapAllocatedSize -------- info.uordblks 當前分配的總空間
android_os_Debug_getNativeHeapFreeSize ------- info.fordblks 總空閑空間

3.3 總結(jié):

Debug主要能獲取到三大塊內(nèi)容:

adb shell dumpsys meminfo pid

1.以Debug.getNativeHeapSize() 為代表的獲取native內(nèi)存信息,底層主要是通過mallinfo獲取的。
2.Debug.getMemoryInfo(mi)執(zhí)行之后,以mi.getTotalPss()為代表的獲取從/proc/PID/smaps統(tǒng)計到的內(nèi)存信息。
3.Debug.getMemoryInfo(mi)執(zhí)行之后,以mi.getMemoryStat("summary.java-heap”)為代表的2數(shù)據(jù)的整合。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容