随着硬件技术的发展,手机的内存越来越大。内存是手机系统中非常重要而且稀缺的资源,做为一个Android的开发人员,我们不能因为存在的增大因此而随意的使用内存,把握好每一块内存,才能让APP的性能达到最优。通常对于内存的管理和优化有以下两种核心原则:
(1)在对象不需要的时候确保能够销毁。
(2)如果对象没有被销毁,则该对象一定是作为可以复用的对象,而不是存在多个。
非RAM存储是指硬盘等永久的存储空间,RAM(random access memory)随机存取存储器,即内存,一般分类如下:
(1)寄存器(Registers):速度最快的存储,用于存储指令、地址和数据,但因其位于处理器的内部,所以我们无法控制。
(2)栈(Stack):存放基本类型的数据,对象的引用和函数地址等,由系统控制。
(3)堆(Heap):存放对象本身和数组(new方式等)。由Java虚拟机的自动垃圾回收器管理。
(4)静态域(static field):存储存储区是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态变量。
(5)常量池(constant pool):虚拟机为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集合,包括直接常量(String、Integer、point常量)和对其他类型,字段和方法的符号引用。
一般开发能够控制的内存基本存在于堆和栈的区域,他们的对比主要表现在以下几点:
(1)栈:定义一个变量时,Java在栈中为这个变量分配内存空间,当该变量退出作用域后,Java会释放其内存,内存空间可立即被另作他用,而堆中new产生的对象和数组,在超出其作用域时,也不会被释放,只有在没有变量指向他们的时候才会变成垃圾,不能再被使用,即使如此,也要等待垃圾回收器回收才会释放内存,这就是为什么Java相较于C语言更占内存的原因。(也可简单理解为:堆由系统GC控制,变量生命周期结束后,由GC系统决定何时回收,存取速度慢。栈由虚拟机控制,变量声明周期结束后,由虚拟机释放该变量占用的内存空间。存取速度快。)
(2)存取速度快(仅次于寄存器),缺点是在栈中的数据大小与生命期是确定的,灵活性比较低。而堆则是运行时的数据区,可动态分配,因此存取速度慢,堆的生存期不必事先告诉编译器,而且Java的垃圾回收器会自动收走不再使用的数据。
(3)栈中的数据可以共享。由编译器完成,有利于节省空间,而堆中一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。例如:需要定义两个变量int a = 1;int b = 1;编译器先处理int a =1;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有1这个值,如果没找到,就将1存放进来,然后将a指向1。接着处理int b = 1;在创建完b的引用变量后,因为在栈中已经有1这个值,便将b直接指向1。这样,就出现了a与b同时均指向1的情况。这时,如果再让a=2;那么编译器会重新搜索栈中是否有2值,如果没有,则将2存放进来,并让a指向2;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。而堆则会影响b的值。
常用的内存耗用名词:VSS/RSS/PSS/USS。
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS
对于应用开着者需要了解内存的一些具体划分:
(1)Shared内存和Private内存,Private内存即是完全属于某个应用独享的那部分已经分配的内存空间,而Shared内存一般系统为了节省内存,将Android中的基础公共库、组件、一些Native Library也归入此,这些内存被所有的进程共享,从指令中获取到的Shared内存大小,也就是每个APP进程所获取到的Shared内存的平均值。之所以这样是因为,Android的进程始于Zygote进程(一切进程的来源,公共资源、组件和Native library都在其中初始化),其他进程通过fork的方式产生新进程,这样新的进程在产生时已经带有了shared内存所预先加载的内容,从而提高进程创建的速度。
(2)Dirty内存和Clean内存,Dirty内存指的是只存储RAM中的内存数据,当RAM清除这些内存后,应用想要再次读取原来内存中的数据必须重新分配后读取,而Clean则恰好相反,保存在Clean内存中的数据同时会缓存在文件中,当RAM清除这些内存后,应用可以从缓存文件中再次读取这些内存中的数据。
(3)dalvik内存是指其所使用的内存。
(4)native是指native堆使用的内存,即C/C++在堆上分配的内存。
(5)other是指除了dalvik和native使用的内存。例如C/C++在栈上分配的内存。
Android中的内存回收主要是GC系统遵循GC Root搜索算法进行回收,其根据是否包含其他对象的引用来判断是否需要GC,GCRoot的对象有下列几种:
(1)class:由System class loader加载的对象。
(2)JNI:jni相关调用的引用、变量、参数。
(3)Thread:活着的线程
(4)Stack:栈中的对象
(5)静态:方法区类的静态属性引用的对象
(6)常量:方法区中的常量引用的对象(final类型)
在Android2.3之前,GC是同步发生的,而且是一次完整的Heap遍历,即会打断正常的应用运行,而且应用内存越大,GC时间越长。2.3之后修改为并发,而且不再一次遍历所有的Heap。GC算法第二次升级是在5.0之后,加快了GC的清理速度,同时在清理的同时扮演管家的角色,为大内存对象分配特殊的地址,方便内存碎片的整理,当应用在后台运行时,GC会对整个app的内存进行对齐,清理小的内存碎片,构成完整的内存区域。