Android需要了解的内存知识

一. 基础知识

Android系统对dalvik的vm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常。

进程的内存空间只是虚拟内存(或者叫作逻辑内存),而程序的运行需要的是实实在在的内存,即物理内存(RAM)。

1. 查看Java内存限制

1.1  通过命令行: adb shell getprop  | grep dalvik.vm

例:
        [dalvik.vm.heapgrowthlimit]: [256m]     每个应用程序最大内存可分配到256m,超过的话,该程式就会崩溃
[dalvik.vm.heapmaxfree]: [8m]
[dalvik.vm.heapminfree]: [512k]
[dalvik.vm.heapsize]: [512m]            单个虚拟机可分配的最大内存512m,android:large_heap=true  大小限制
[dalvik.vm.heapstartsize]: [8m]         表示应用程序启动后为其分配的初始大小为8m

[dalvik.vm.heaptargetutilization]: [0.75]

说明:

[dalvik.vm.heapstartsize]

堆分配的初始大小,调整这个值会影响到应用的流畅性和整体ram消耗。

这个值越小,系统ram消耗越慢,但是由于初始值较小,一些较大的应用需要扩张这个堆,

从而引发gc和堆调整的策略,使应用反应更慢。相反,这个值越大系统ram消耗越快,但是程序更流畅。


[dalvik.vm.heapgrowthlimit]

极限堆大小,dvm heap是可增长的,但是正常情况下dvm heap的大小是不会超过dalvik.vm.heapgrowthlimit的值。

如果受控的应用dvm heap size超过该值,则将引发oom。


[dalvik.vm.heapsize]

使用大堆时,极限堆大小。一旦dalvik heap size超过这个值,直接引发oom。

在android开发中,如果要使用大堆,需要在manifest中指定android:largeHeap为true。

这样dvm heap最大可达dalvik.vm.heapsize。


[dalvik.vm.heaptargetutilization]: [0.75]   

可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,

虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。


[dalvik.vm.heapminfree]: [512k]

堆最小空闲内存


[dalvik.vm.heapmaxfree]: [8m]

堆最大空闲内存


dalvik.vm.heaptargetutilization、dalvik.vm.heapminfree、dalvik.vm.heapmaxfree

对GC的垃圾回收会有影响


上面的几个参数是与虚拟机的内存分配相关的,虚拟机的内存分配过程是下面这样的:

(1)  首先判断一下需要申请的size是不是过大,如果申请的size超过了堆的最大限制,则转入步骤6

(2)  尝试分配,如果成功则返回,失败则转入步骤3

(3)  判断是否gc正在进行垃圾回收,如果正在进行则等待回收完成之后,尝试分配。

   如果成功则返回,失败则转入步骤4

(4)  自己启动gc进行垃圾回收,这里gcForMalloc的参数是false。

   所以不会回收软引用,回收完成后尝试分配,如果成功则返回,失败则转入步骤5

(5)  调用dvmHeapSourceAllocAndGrow尝试分配,这个函数会扩张堆。

   所以heap startup的时候可以给一个比较小的初始堆,实在不够用再调用它进行扩张

(6)  进入回收软引用阶段,这里gcForMalloc的参数是ture,所以需要回收软引用。

   然后调用dvmHeapSourceAllocAndGrow尝试分配,如果失败则抛出OOM。

1.2 通过代码打印

        activityManager.getMemoryClass() = 256

activityManager.getLargeMemoryClass() = 512

2. 使用 adb shell procrank  查看进程内存信息

    # procrank
  PID       Vss      Rss      Pss      Uss  cmdline
1432  2543816K  228396K  160176K  151760K  system_server
2146  1821172K  178136K  112690K  106044K  com.android.systemui
4834  1862856K  135344K   87604K   70700K  cn.example.photoeditor
3093  1854340K  150916K   79161K   66884K  cn.example.launcher
3486  1774200K  105420K   43758K   39316K  cn.example.gallery3d
  788   157648K   45772K   37486K   35244K  /system/bin/mm-qcamera-daemon

2382  1781900K   91416K   34330K   31604K  com.android.phone


   VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

   RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)

   PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

   USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)(对判断应用内存泄漏具有参考性)

    一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS

   

3. 使用 adb shell dumpsys meminfo + packagename/pid 查看应用内存信息

   Davik heap —— Java申请的内存

   Native heap —— C/C++申请的内存



二. 什么是内存泄漏:内存不在GC的掌控之内了。

    了解几个问题

   1.  垃圾回收机制GC
总结java的GC内存回收机制:某对象不再有任何的引用的时候才会进行回收。

GC回收机制的原理:了解JVM虚拟机

可以作为GC Root引用点的是:
1. JavaStack中的引用的对象。
2. 方法区中静态引用指向的对象。
3. 方法区中常量引用指向的对象。
4. Native方法中JNI引用的对象。
5. Thread--“活着的”线程。

    2. 怎么判断一个对象是垃圾对象?
这是一个主观的判断。

内存泄漏多了容易导致OOM——内存溢出,app会崩溃。

三. 确定我们项目当中或者某几个类里面是否存在内存泄漏。

     粗略判断。

    Android Monitor --> System Information --> MemoryUsage查看Objects里面是否有没有被释放的views或者Activity的数量是否为0
    
    命令行模式:adb shell dumpsys meminfo 包名 -d

四. 确定内存泄漏的大致范围:

    AndroidStudio方式:看Memory Monitor工具。

    检查一个一个的工作。(比如Activity的跳转)  

    反复多次执行某一个操作,不断地通过这个工具查看大概情况

    前后两个内存变化增加了不少。
   

五. 更仔细地查找泄漏的位置。

     在AS里面使用Heap SnapShot工具(堆栈快照)


六. 使用更高级的分析工具来更具体的找到这个泄漏。MAT

    举例:
单例模式:
CommUtil instance;
instance = new CommUtil(mContext);  //错误
instance = new CommUtil(mContext.getApplicationContext()); //正确
原因:不使用Activity的上下文,而是使用Application的上下文。
因为Application的生命周期长,进程退出的时候才会销毁,和Static的CommUtil的生命周期一样长。

七. 应用程序如何绕过dalvikvm heapsize的限制

    1. 创建子进程: 使用android:process标签

    2. 使用jni在native heap上申请空间(推荐使用)

        nativeheap的增长并不受dalvik vm heapsize的限制

    3. 使用显存(操作系统预留RAM的一部分作为显存)

       使用OpenGL textures等API,texture memory不受dalvik vm heapsize限制


八. 运行时打印内存信息
       // 3.0-7.1 bitmap pixel allocate in Dalvik Heap
       // 2.3, 8.0 bitmap pixel allocate in Native Heap
private void printMemory() {
Runtime runtime = Runtime.getRuntime();
long max = runtime.maxMemory() / 1024;
long used = 0;
if (Build.VERSION.SDK_INT >= 11 && Build.VERSION.SDK_INT <= 25) {
used = runtime.totalMemory() / 1024;
// java memory
    } else {
    used = Debug.getNativeHeapAllocatedSize() / 1024;
// native memory
}

}


性能优化——内存泄漏优化

Google文档
https://developer.android.com/topic/performance/graphics/manage-memory.html
https://www.cnblogs.com/yezhennan/p/5442557.html
http://blog.csdn.net/gemmem/article/details/8920039?utm_source=tuicool


阅读更多

没有更多推荐了,返回首页