美文网首页
几种内存信息获取方式源码分析

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

作者: Stan_Z | 来源:发表于2020-12-22 22:15 被阅读0次

    这里简单总结下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数据的整合。

    相关文章

      网友评论

          本文标题:几种内存信息获取方式源码分析

          本文链接:https://www.haomeiwen.com/subject/brtsnktx.html