推荐文章:Android进程与内存及内存泄漏

Android之内存管理

1.1 Dalvik
Dalvik虚拟机是Android程序的虚拟机,是Android中Java程序的运行基础。其指令集基于寄存器架构,执行其特有的文件格式——dex字节码来完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收等重要功能。
Dalvik虚拟机的内存大体上可以分为 Java Object Heap、Bitmap Memory和Native Heap三种。
Java Object Heap:用于分配对象
Bitmap Memory:用来处理图像,≥Android 3.0, 归到Object Heap中
Native Heap: malloc分配,受系统限制

1.2 查看单个进程最大内存限制

Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM。

这个属性值是定义在/system/build.prop文件中的
dalvik.vm.heapstartsize=8m   //它表示堆分配的初始大小

它会影响到整个系统对RAM的使用程度,和第一次使用应用时的流畅程度。它值越小,系统ram消耗越慢,但一些较大应用一开始不够用,需要调用gc和堆调整策略,导致应用反应较慢。它值越大,这个值越大系统ram消耗越快,但是应用更流畅。

dalvik.vm.heapgrowthlimit=64m // 单个应用可用最大内存

主要对应的是这个值,它表示单个进程内存被限定在64m,即程序运行过程中实际只能使用64m内存,超出就会报OOM。(仅仅针对dalvik堆,不包括native堆)

dalvik.vm.heapsize=384m  //heapsize参数表示单个进程可用的最大内存,

但如果存在heapgrowthlimit参数,则以heapgrowthlimit为准.
heapsize表示不受控情况下的极限堆,表示单个虚拟机或单个进程可用的最大内存。而android上的应用是带有独立虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机(这样设计就会在单个程序崩溃的情况下不会导致整个系统的崩溃)。
注意:在设置了heapgrowthlimit的情况下,单个进程可用最大内存为heapgrowthlimit值。在android开发中,如果要使用大堆,需要在manifest中指定android:largeHeap为true,这样dvm heap最大可达heapsize。

Android不同设备单个进程可用内存是不一样的,可以查看/system/build.prop文件。

# This is a high density device with more memory, so larger vm heaps for it.
dalvik.vm.heapsize=24m

上面heapsize参数表示单个进程可用的最大内存,单如果存在如下参数:

dalvik.vm.heapgrowthlimit=16m

largeheaplimit参数表示单个进程内存被限定在16m,即程序运行过程中实际只能使用16m内存,不过有一个办法可以解决,编辑AndroidManifest.xml中的Application节点,增加属性largeheap="true"参数.

 // 应用程序最大可用内存  dalvik.vm.heapsize的值我的手机是256M
        long maxMemory = ((int) Runtime.getRuntime().maxMemory())/1024/1024;  
        //应用程序已获得内存  dalvik.vm.heapgrowthlimit的值我的手机是25M
        long totalMemory = ((int) Runtime.getRuntime().totalMemory())/1024/1024;  
        //应用程序已获得内存中未使用内存  
        long freeMemory = ((int) Runtime.getRuntime().freeMemory())/1024/1024; 
        
	        System.out.println("---> maxMemory="+maxMemory+"M,totalMemory="+totalMemory+"M,freeMemory="+freeMemory+"M");
	    Toast.makeText(MainActivity.this, "---> maxMemory="+maxMemory+"M,totalMemory="+totalMemory+"M,freeMemory="+freeMemory+"M", Toast.LENGTH_SHORT).show();

JAVA的内存管理
大家都知道,android应用层是由java开发的,android的davlik虚拟机与jvm也类似,只不过它是基于寄存器的。因此要了解android的内存管理就必须得了解java的内存分配和垃圾回收机制。
在java中,是通过new关键字来为对象分配内存的,而内存的释放是由垃圾收集器(GC)来回收的,工程师在开发的过程中,不需要显式的去管理内存。但是这样有可能在不知不觉中就会浪费了很多内存,最终导致java虚拟机花费很多时间去进行垃圾回收,更严重的是造成JVM的OOM。因此,java工程师还是有必要了解JAVA的内存分配和垃圾回收机制。

1,内存结构

                                           

上面这张图是JVM的结构图,它主要四个部分组成:Class Loader子系统和执行引擎,运行时方法区和本地方法区,我们主要来看下RUNTIME DATA AREA区,也就是我们常说的JVM内存。从图中可以看出,RUNTIMEDATA AREA区主要由5个部分组成:
      Method Area:被装载的class的元信息存储在Method Area中,它是线程共享的
     Heap(堆):一个java虚拟机实例中只存在一个堆空间,存放一些对象信息,它是线程共享的
    Java栈: java虚拟机直接对java栈进行两种操作,以帧为单位的压栈和出栈(非线程共享)
     程序计数器(非线程共享)
    本地方法栈(非线程共享)

2,JVM的垃圾回收(GC)

       


JVM的垃圾原理是这样的,它把对象分为年轻代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的垃圾回收算法
年轻代(Young)
年轻代分为三个区,一个eden区,两个Survivor区。程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当此Survivor区的对象占用空间满了时,此区存活的对象又被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到年老代。
年老代(Tenured)
年老代存放的是上面年轻代复制过来的对象,也就是在年轻代中还存活的对象,并且区满了复制过来的。一般来说,年老代中的对象生命周期都比较长。
持久代(Perm)
用于存放静态的类和方法,持久代对垃圾回收没有显著的影响。

Android中内存泄露监测

在了解了JVM的内存管理后,我们再回过头来看看,在android中应该怎样来监测内存,从而看在应用中是否存在内存分配和垃圾回收问题而造成内存泄露情况。
在android中,有一个相对来说还不错的工具,可以用来监测内存是否存在泄露情况:DDMS—Heap


选择DDMS视图,并打开Devices视图和Heap视图
点击选择要监控的进程,比如:上图中我选择的是system_process
选中Devices视图界面上的"update heap" 图标
点击Heap视图中的"Cause GC" 按钮(相当于向虚拟机发送了一次GC请求的操作)
在Heap视图中选择想要监控的Type,一般我们会观察dataobject的 total size的变化,正常情况下total size的值会稳定在一个有限的范围内,也就说程序中的代码良好,没有造成程序中的对象不被回收的情况。如果代码中存在没有释放对象引用的情况,那么data object的total size在每次GC之后都不会有明显的回落,随着操作次数的增加而total size也在不断的增加。(说明:选择好data object后,不断的操作应用,这样才可以看出total size的变化)。如果totalsize确实是在不断增加而没有回落,说明程序中有没有被释放的资源引用。那么我们应该怎么来定位呢?

Android中内存泄露定位

Mat(memory analyzer tools)是我们常用的用来定位内存泄露的工具,如果你使用ADT,并且安装了MAT的eclipse插件,你需要做的是进入DDMS视图的Devices视图


点击"dump HPROF file"按钮,然后使用MAT分析下载下来的文件


关于MAT的使用可以参考:http://www.blogjava.net/rosen/archive/2010/06/13/323522.html
这位兄弟写的比较详细。

转自:http://blog.csdn.net/xieqibao/article/details/6707519

android的内存优化看另一篇文章:

Android内存优化的几种方案


3、为什么会内存泄露(Memory Leak)?
android通过android虚拟机来管理内存,程序员只管申请内存创建对象,创建完不再需要关心怎么释放对象内存,一切由虚拟机帮你搞定,然而虚拟机回收对象是有条件的。这里简单叙述下java内存管理机制,java虚拟机维护着一张当前对象关系的object tree,当GC发生时,虚拟机会从GC Roots 开始去扫描当前的对象树,发现通过任何reference chain(引用链)无法访问某个对象的时候,该对象即被回收。名词GC Roots正是分析这一过程的起点,例如JVM自己确保了对象的可到达性(那么JVM就是GC Roots),所以GC Roots就是这样在内存中保持对象可到达性的,一旦不可到达,即被回收。通常GC Roots是一个在current thread(当前线程)的call stack(调用栈)上的对象(例如方法参数和局部变量),或者是线程自身或者是system class loader(系统类加载器)加载的类以及native code(本地代码)保留的活动对象。所以GC Roots是分析对象为何还存活于内存中的利器。知道了什么样的对象GC才会回收后,再来学习下对象引用都包含哪些吧。
Java中包含4种对象引用:
   强引用: 通常我们编写的代码都是Strong Ref,eg :Person person = new Person("sunny");不管系统资源有多紧张,强引用的对象都绝对不会被回收,即使他以后不再用到。
   软引用只要有足够的内存,就一直保持对象。一般可用来实现缓存,通过java.lang.r.efSoftReference类实现。内存非常紧张的时候会被回收,其他时候不会被回收,所以在使用之前需要判空,从而判断当前时候已经被回收了。
   弱引用:通过WeakReference类实现,eg : WeakReference p = new WeakReference(new Person("Rain"));不管内存是否足够,系统垃圾回收时必定会回收。
   虚引用:不能单独使用,主要是用于追踪对象被垃圾回收的状态。通过PhantomReference类和引用队列ReferenceQueue类联合使用实现。

4、为什么会发生OOM(Out Of Memory)?
  OOM:即OutOfMemoery,顾名思义就是指内存溢出了。之前我们知道Android的应用程序所能申请的最大内存都是有限的,OOM是指APP向系统申请内存的请求超过了应用所能有的最大阀值的内存,系统无法再分配多余的空间,就会造成OOM error。在Android平台下,除了之前所说的持续发生了内存泄漏(Memory Leak),累积到一定程度导致OOM的情况以外,也有一次性申请很多内存,比如说一次创建大的数组或者是载入大的文件如图片的时候。实际中很多情况就是出现在图片不当处理加载的时候。

5、常见的MemoryLeak分析
后来看到了更多的MemoryLeak相关的知识,有了更多的实践经验,
就此小小总结了一下,见 Android 内存优化 (防Memory Leak)Android内存优化

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐