转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!
html
写在最前:java
本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各类内存零散知识点进行汇总、挑选、简化后整理而成。android
因此我将本文定义为一个工具类的文章,若是你在Android开发中遇到关于内存问题,或者立刻要参加面试,或者就是单纯的学习或复习一下内存相关知识,都欢迎阅读。(本文最后我会尽可能列出所参考的文章)。git
通常Java在内存分配时会涉及到如下区域:程序员
寄存器(Registers):速度最快的存储场所,由于寄存器位于处理器内部,咱们在程序中没法控制github
栈(Stack):存放基本类型的数据和对象的引用,但对象自己不存放在栈中,而是存放在堆中面试
堆(Heap):堆内存用来存放由new建立的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理。正则表达式
静态域(static field): 静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量算法
常量池(constant pool):虚拟机必须为每一个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其余类型,字段和方法的符号引用。shell
非RAM存储:硬盘等永久存储空间
堆栈特色对比:
因为篇幅缘由,下面只简单的介绍一下堆栈的一些特性。
栈:当定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该做用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间能够当即被另做他用。
堆:当堆中的new产生数组和对象超出其做用域后,它们不会被释放,只有在没有引用变量指向它们的时候才变成垃圾,不能再被使用。即便这样,所占内存也不会当即释放,而是等待被垃圾回收器收走。这也是Java比较占内存的缘由。
栈:存取速度比堆要快,仅次于寄存器。但缺点是,存在栈中的数据大小与生存期必须是肯定的,缺少灵活性。
堆:堆是一个运行时数据区,能够动态地分配内存大小,所以存取速度较慢。也正由于这个特色,堆的生存期没必要事先告诉编译器,并且Java的垃圾收集器会自动收走这些再也不使用的数据。
栈:栈中的数据能够共享, 它是由编译器完成的,有利于节省空间。
例如:须要定义两个变量int a = 3;int b = 3;
编译器先处理int a = 3;首先它会在栈中建立一个变量为a的引用,而后查找栈中是否有3这个值,若是没找到,就将3存放进来,而后将a指向3。接着处理int b = 3;在建立完b的引用变量后,由于在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的状况。这时,若是再让a=4;那么编译器会从新搜索栈中是否有4值,若是没有,则将4存放进来,并让a指向4;若是已经有了,则直接将a指向这个地址。所以a值的改变不会影响到b的值。
堆:例如上面栈中a的修改并不会影响到b, 而在堆中一个对象引用变量修改了这个对象的内部状态,会影响到另外一个对象引用变量。
内存耗用名词解析:
VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
通常来讲内存占用大小有以下规律:VSS >= RSS >= PSS >= USS
OOM:
内存泄露能够引起不少的问题:
1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)
2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)
3.直接崩溃(OutOfMemoryError)
ANDROID内存面临的问题:
1.有限的堆内存,原始只有16M
2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样
3.程序不能直接控制
4.支持后台多任务处理(multitasking)
5.运行在虚拟机之上
5R:
本文主要经过以下的5R方法来对ANDROID内存进行优化:
1.Reckon(计算)
首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆
2.Reduce(减小)
消耗更少的资源
3.Reuse(重用)
当第一次使用完之后,尽可能给其余的使用
5.Recycle(回收)
返回资源给生产流
4.Review(检查)
回顾检查你的程序,看看设计或代码有什么不合理的地方。
Reckon (计算):
了解本身应用的内存使用状况是颇有必要的。若是当内存使用太高的话就须要对其进行优化,由于更少的使用内存能够减小ANDROID系统终止咱们的进程的概率,也能够提升多任务执行效率和体验效果。
下面从系统内存(system ram)和堆内存(heap)两个方面介绍一些查看和计算内存使用状况的方法:
System Ram(系统内存):
观察和计算系统内存使用状况,可使用Android提供给咱们的两个工具procstats,meminfo。他们一个侧重于后台的内存使用,另外一个是运行时的内存使用。
Heap(堆内存):
在程序中可使用以下的方法去查询内存使用状况
ActivityManager#getMemoryClass()
查询可用堆内存的限制
3.0(HoneyComb)以上的版本能够经过largeHeap=“true”来申请更多的堆内存(不过这算做“做弊”)
android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
获得的MemoryInfo中能够查看以下Field的属性:
android.os.Debug#getNativeHeapSize()
返回的是当前进程navtive堆自己总的内存大小
android.os.Debug#getNativeHeapAllocatedSize()
返回的是当前进程navtive堆中已使用的内存大小
android.os.Debug#getNativeHeapFreeSize()
返回的是当前进程navtive堆中已经剩余的内存大小
Memory Analysis Tool(MAT):
一般内存泄露分析被认为是一件颇有难度的工做,通常由团队中的资深人士进行。不过,今天咱们要介绍的 MAT(Eclipse Memory Analyzer)被认为是一个“傻瓜式“的堆转储文件分析工具,你只须要轻轻点击一下鼠标就能够生成一个专业的分析报告。
以下图:
关于详细的MAT使用我推荐下面这篇文章:使用 Eclipse Memory Analyzer 进行堆转储文件分析
OOM:
内存泄露能够引起不少的问题:
1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)
2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)
3.直接崩溃(OutOfMemoryError)
ANDROID内存面临的问题:
1.有限的堆内存,原始只有16M
2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样
3.程序不能直接控制
4.支持后台多任务处理(multitasking)
5.运行在虚拟机之上
5R:
本文主要经过以下的5R方法来对ANDROID内存进行优化:
1.Reckon(计算)
首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆
2.Reduce(减小)
消耗更少的资源
3.Reuse(重用)
当第一次使用完之后,尽可能给其余的使用
5.Recycle(回收)
返回资源给生产流
4.Review(检查)
回顾检查你的程序,看看设计或代码有什么不合理的地方。
Reckon:
关于内存简介,和Reckon(内存计算)的内容请看上一篇文章:ANDROID内存优化(大汇总——上)
Reduce :
Reduce的意思就是减小,直接减小内存的使用是最有效的优化方式。
下面来看看有哪些方法能够减小内存使用:
图片显示:
咱们须要根据需求去加载图片的大小。
例如在列表中仅用于预览时加载缩略图(thumbnails )。
只有当用户点击具体条目想看详细信息的时候,这时另启动一个fragment/activity/对话框等等,去显示整个图片
图片大小:
直接使用ImageView显示bitmap会占用较多资源,特别是图片较大的时候,可能致使崩溃。
使用BitmapFactory.Options设置inSampleSize, 这样作能够减小对系统资源的要求。
属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即若是这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。
图片像素:
图片回收:
使用Bitmap事后,就须要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间,而不要等Android系统来进行释放。
下面是释放Bitmap的示例代码片断。
捕获异常:
通过上面这些优化后还会存在报OOM的风险,因此下面须要一道最后的关卡——捕获OOM异常:
修改对象引用类型:
引用类型:
引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。
强引用(strong reference)
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足问题。
软引用(SoftReference)
只有内存不够时才回收,经常使用于缓存;当内存达到一个阀值,GC就会去回收它;
弱引用(WeakReference)
弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
"虚引用"顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收。
软引用和弱引用的应用实例:
注意:对于SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,如今已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,因此下面的内容能够选择忽略。
在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大并且声明周期较长的对象时候,能够尽可能应用软引用和弱引用技术。
下面以使用软引用为例来详细说明(弱引用的使用方式与软引用是相似的):
假设咱们的应用会用到大量的默认图片,并且这些图片不少地方会用到。若是每次都去读取图片,因为读取文件须要硬件操做,速度较慢,会致使性能较低。因此咱们考虑将图片缓存起来,须要的时候直接从内存中读取。可是,因为图片占用内存空间比较大,缓存不少图片须要不少的内存,就可能比较容易发生OutOfMemory异常。这时,咱们能够考虑使用软引用技术来避免这个问题发生。
首先定义一个HashMap,保存软引用对象。
须要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象以后,get方法将返回null。因此在获取软引用对象的代码中,必定要判断是否为null,以避免出现NullPointerException异常致使应用崩溃。
到底何时使用软引用,何时使用弱引用呢?
我的认为,若是只是想避免OutOfMemory异常的发生,则可使用软引用。若是对于应用的性能更在乎,想尽快回收一些占用内存比较大的对象,则可使用弱引用。
还有就是能够根据对象是否常用来判断。若是该对象可能会常用的,就尽可能用软引用。若是该对象不被使用的可能性更大些,就能够用弱引用。
另外,和弱引用功能相似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收之后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。
其余小tips:
对常量使用static final修饰符
让咱们来看看这两段在类前面的声明:
static int intVal = 42;
static String strVal = "Hello, world!";
编译器会生成一个叫作clinit的初始化类的方法,当类第一次被使用的时候这个方法会被执行。方法会将42赋给intVal,而后把一个指向类中常量表 的引用赋给strVal。当之后要用到这些值的时候,会在成员变量表中查找到他们。 下面咱们作些改进,使用“final”关键字:
static final int intVal = 42;
static final String strVal = "Hello, world!";
如今,类再也不须要clinit方法,由于在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个字符串常量,而不是使用成员变量。
将一个方法或类声明为final不会带来性能的提高,可是会帮助编译器优化代码。举例说,若是编译器知道一个getter方法不会被重载,那么编译器会对其采用内联调用。
你也能够将本地变量声明为final,一样,这也不会带来性能的提高。使用“final”只能使本地变量看起来更清晰些(可是也有些时候这是必须的,好比在使用匿名内部类的时候)。
静态方法代替虚拟方法
若是不须要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的作法,由于你能够从方法声明中看出调用该方法不须要更新此对象的状态。
减小没必要要的全局变量
尽可能避免static成员变量引用资源耗费过多的实例,好比Context
避免建立没必要要的对象
最多见的例子就是当你要频繁操做一个字符串时,使用StringBuffer代替String。
对于全部全部基本类型的组合:int数组比Integer数组好,这也归纳了一个基本事实,两个平行的int数组比 (int,int)对象数组性能要好不少。
整体来讲,就是避免建立短命的临时对象。减小对象的建立就能减小垃圾收集,进而减小对用户体验的影响。
避免使用浮点数
一般的经验是,在Android设备中,浮点数会比整型慢两倍。
使用实体类比接口好
假设你有一个HashMap对象,你能够将它声明为HashMap或者Map:
Map map1 = new HashMap(); HashMap map2 = new HashMap();
哪一个更好呢?
按照传统的观点Map会更好些,由于这样你能够改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,可是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。若是HashMap彻底适合你的程序,那么使用Map就没有什么价值。若是有些地方你不能肯定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(固然公共API是一个例外:一个好的API经常会牺牲一些性能)
避免使用枚举
枚举变量很是方便,但不幸的是它会牺牲执行的速度和并大幅增长文件体积。
使用枚举变量可让你的API更出色,并能提供编译时的检查。因此在一般的时候你毫无疑问应该为公共API选择枚举变量。可是当性能方面有所限制的时候,你就应该避免这种作法了。
for循环
访问成员变量比访问本地变量慢得多,以下面一段代码:
永远不要在for的第二个条件中调用任何方法,以下面一段代码:
对上面两个例子最好改成:
了解并使用类库
选择Library中的代码而非本身重写,除了一般的那些缘由外,考虑到系统空闲时会用汇编代码调用来替代library方法,这可能比JIT中生成的等价的最好的Java代码还要好。
当你在处理字串的时候,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊实现的方法。这些方法都是使用C/C++实现的,比起Java循环快10到100倍。
System.arraycopy方法在有JIT的Nexus One上,自行编码的循环快9倍。
android.text.format包下的Formatter类,提供了IP地址转换、文件大小转换等方法;DateFormat类,提供了各类时间转换,都是很是高效的方法。
TextUtils类,对于字符串处理Android为咱们提供了一个简单实用的TextUtils类,若是处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类
高性能MemoryFile类,不少人抱怨Android处理底层I/O性能不是很理想,若是不想使用NDK则能够经过MemoryFile类实现高性能的文件读写操做。MemoryFile适用于哪些地方呢?对于I/O须要频繁操做的,主要是和外部存储相关的I/O操做,MemoryFile经过将 NAND或SD卡上的文件,分段映射到内存中进行修改处理,这样就用高速的RAM代替了ROM或SD卡,性能天然提升很多,对于Android手机而言同时还减小了电量消耗。该类实现的功能不是不少,直接从Object上继承,经过JNI的方式直接在C底层执行。
Reuse:
Bitmap缓存分为两种:
一种是内存缓存,一种是硬盘缓存。
内存缓存(LruCache):
以牺牲宝贵的应用内存为代价,内存缓存提供了快速的Bitmap访问方式。系统提供的LruCache类是很是适合用做缓存Bitmap任务的,它将最近被引用到的对象存储在一个强引用的LinkedHashMap中,而且在缓存超过了指定大小以后将最近不常使用的对象释放掉。
注意:之前有一个很是流行的内存缓存实现是SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,然而如今已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,这使得上述的方案至关无效。
硬盘缓存(DiskLruCache):
一个内存缓存对加速访问最近浏览过的Bitmap很是有帮助,可是你不能局限于内存中的可用图片。GridView这样有着更大的数据集的组件能够很轻易消耗掉内存缓存。你的应用有可能在执行其余任务(如打电话)的时候被打断,而且在后台的任务有可能被杀死或者缓存被释放。一旦用户从新聚焦(resume)到你的应用,你得再次处理每一张图片。
在这种状况下,硬盘缓存能够用来存储Bitmap并在图片被内存缓存释放后减少图片加载的时间(次数)。固然,从硬盘加载图片比内存要慢,而且应该在后台线程进行,由于硬盘读取的时间是不可预知的。
注意:若是访问图片的次数很是频繁,那么ContentProvider可能更适合用来存储缓存图片,例如Image Gallery这样的应用程序。
更多关于内存缓存和硬盘缓存的内容请看Google官方教程 https://developer.android.com/develop/index.html1. Android-Universal-Image-Loader 图片缓存
目前使用最普遍的图片缓存,支持主流图片缓存的绝大多数特性。
项目地址:https://github.com/nostra13/Android-Universal-Image-Loader
2. picasso square开源的图片缓存
项目地址:https://github.com/square/picasso
特色:(1)能够自动检测adapter的重用并取消以前的下载
(2)图片变换
(3)能够加载本地资源
(4)能够设置占位资源
(5)支持debug模式
3. ImageCache 图片缓存,包含内存和Sdcard缓存
项目地址:https://github.com/Trinea/AndroidCommon
特色:
(1)支持预取新图片,支持等待队列
(2)包含二级缓存,可自定义文件名保存规则
(3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法
(4)可方便的保存及初始化恢复数据
(5)支持不一样类型网络处理
(6)可根据系统配置初始化缓存等
对象池:
对象池使用的基本思路是:将用过的对象保存起来,等下一次须要这种对象的时候,再拿出来重复使用,从而在必定程度上减小频繁建立对象所形成的开销。 并不是全部对象都适合拿来池化――由于维护对象池也要形成必定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能下降的状况。可是对于生成时开销可观的对象,池化技术就是提升性能的有效策略了。
线程池的基本思想仍是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样能够避免反复建立线程对象所带来的性能开销,节省了系统的资源。
好比:一个应用要和网络打交道,有不少步骤须要访问网络,为了避免阻塞主线程,每一个步骤都建立个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只须要创一个线程池,把这些步骤像任务同样放进线程池,在程序销毁时只要调用线程池的销毁函数便可。
java提供了ExecutorService和Executors类,咱们能够应用它去创建线程池。
一般能够创建以下4种:
更多关于线程池的内容我推荐这篇文章: http://www.xuanyusong.com/archives/2439
要根据状况适度使用缓存,由于内存有限。
能保存路径地址的就不要存放图片数据,不常用的尽可能不要缓存,不用时就清空。
OOM:
内存泄露能够引起不少的问题:
1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)
2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)
3.直接崩溃(OutOfMemoryError)
ANDROID内存面临的问题:
1.有限的堆内存,原始只有16M
2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样
3.程序不能直接控制
4.支持后台多任务处理(multitasking)
5.运行在虚拟机之上
5R:
本文主要经过以下的5R方法来对ANDROID内存进行优化:
1.Reckon(计算)
首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆
2.Reduce(减小)
消耗更少的资源
3.Reuse(重用)
当第一次使用完之后,尽可能给其余的使用
5.Recycle(回收)
回收资源
4.Review(检查)
回顾检查你的程序,看看设计或代码有什么不合理的地方。
内存简介,Reckon(计算):
关于内存简介,和Reckon的内容请看:ANDROID内存优化(大汇总——上)
Reduce(减小) ,Reuse(重用):
关于Reduce,和Reuse的内容请看:ANDROID内存优化(大汇总——中)
Recycle(回收):
Recycle(回收),回收能够说是在内存使用中最重要的部分。由于内存空间有限,不管你如何优化,如何节省内存总有用完的时候。而回收的意义就在于去清理和释放那些已经闲置,废弃再也不使用的内存资源和内存空间。
由于在Java中有垃圾回收(GC)机制,因此咱们平时都不会太关注它,下面就来简单的介绍一下回收机制:
垃圾回收(GC):
Java垃圾回收器:
在C,C++或其余程序设计语言中,资源或内存都必须由程序员自行声明产生和回收,不然其中的资源将消耗,形成资源的浪费甚至崩溃。但手工回收内存每每是一项复杂而艰巨的工做。
因而,Java技术提供了一个系统级的线程,即垃圾收集器线程(Garbage Collection Thread),来跟踪每一块分配出去的内存空间,当Java 虚拟机(Java Virtual Machine)处于空闲循环时,垃圾收集器线程会自动检查每一快分配出去的内存空间,而后自动回收每一快能够回收的无用的内存块。
做用:
1.清除不用的对象来释放内存:
采用一种动态存储管理技术,它自动地释放再也不被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象再也不被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。
2.消除堆内存空间的碎片:
因为建立对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。
垃圾回收器优势:
1.减轻编程的负担,提升效率:
使程序员从手工回收内存空间的繁重工做中解脱了出来,由于在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾收集机制可大大缩短期。
2.它保护程序的完整性:
所以垃圾收集是Java语言安全性策略的一个重要部份。
垃圾回收器缺点:
1.占用资源时间:
Java虚拟机必须追踪运行程序中有用的对象, 并且最终释放没用的对象。这一个过程须要花费处理器的时间。
2.不可预知:
垃圾收集器线程虽然是做为低优先级的线程运行,但在系统可用内存量太低的时候,它可能会突发地执行来挽救内存资源。固然其执行与否也是不可预知的。
3.不肯定性:
不能保证一个无用的对象必定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中必定会执行。
一样也没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪个会被首先收集。
4.不可操做
垃圾收集器不能够被强制执行,但程序员能够经过调用System. gc方法来建议执行垃圾收集器。
finalize():
每个对象都有一个finalize方法,这个方法是从Object类继承来的。
当垃圾回收肯定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
Java 技术容许使用finalize方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。一旦垃圾回收器准备好释放对象占用的空间,将首先调用其finalize()方法,而且在下一次垃圾回收动做发生时,才会真正回收对象占用的内存。
简单的说finalize方法是在垃圾收集器删除对象以前对这个对象调用的
System.gc():
咱们能够调用System.gc方法,建议虚拟机进行垃圾回收工做(注意,是建议,但虚拟机会不会这样干,咱们也没法预知!)
下面来看一个例子来了解finalize()和System.gc()的使用:
答案是:不必定!
由于不管是设置gc的引用为null仍是调用System.gc()方法都只是"建议"垃圾回收器进行垃圾回收,可是最终全部权还在垃圾回收器手中,它会不会进行回收咱们没法预知!
垃圾回收面试题:
最后经过网上找到的3道面试题来结束垃圾回收的内容。
面试题一:
问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准?
资源的回收:
刚才讲了一堆理论的东西,下面来点实际能用上的,资源的回收:
Thread(线程)回收:
线程中涉及的任何东西GC都不能回收(Anything reachable by a thread cannot be GC'd ),因此线程很容易形成内存泄露。
以下面代码所示:
最后的结果是线程并不会被回收,它会一直的运行下去。
由于运行中的线程是称之为垃圾回收根(GC Roots)对象的一种,不会被垃圾回收。当垃圾回收器判断一个对象是否可达,老是使用垃圾回收根对象做为参考点。
Cursor(游标)回收:
Cursor是Android查询数据后获得的一个管理数据集合的类,在使用结束之后。应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。而且Android明显是倾向于编程者手动的将Cursor close掉,由于在源代码中咱们发现,若是等到垃圾回收器来回收时,会给用户以错误提示。
因此咱们使用Cursor的方式通常以下:
Receiver(接收器)回收
Stream/File(流/文件)回收:
主要针对各类流,文件资源等等如:
InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap图片等操做等都应该记得显示关闭。
和以前介绍的Cursor道理相似,就很少说了。
Review:
Review(回顾,检查),你们都知道Code Review的重要性。而这里我说的Review和Code Review差很少,主要目的就是检查代码中存在的不合理和能够改进的地方,固然这个Review须要你们本身来作啦。
Code Review(代码检查):
Code Review主要检查代码中存在的一些不合理或能够改进优化的地方,你们能够参考以前写的Reduce,Reuse和Recycle都是侧重讲解这方面的。
UI Review(视图检查):
Android对于视图中控件的布局渲染等会消耗不少的资源和内存,因此这部分也是咱们须要注意的。
减小视图层级:
如上图你们能够看到,hierarchyviewer能够很是清楚的看到当前视图的层级结构,而且能够查看视图的执行效率(视图上的小圆点,绿色表示流畅,黄色和红色次之),因此咱们能够很方便的查看哪些view可能会影响咱们的性能从而去进一步优化它。
hierarchyviewer还提供另一种列表式的查看方式,能够查看详细的屏幕画面,具体到像素级别的问题均可以经过它发现。
ViewStub标签
此标签可使UI在特殊状况下,直观效果相似于设置View的不可见性,可是其更大的意义在于被这个标签所包裹的Views在默认状态下不会占用任何内存空间。
include标签
能够经过这个标签直接加载外部的xml到当前结构中,是复用UI资源的经常使用标签。
merge标签
它在优化UI结构时起到很重要的做用。目的是经过删减多余或者额外的层级,从而优化整个Android Layout的结构。
(注意:灵活运用以上3个标签能够有效减小视图层级,具体使用你们能够上网搜搜)
布局用Java代码比写在XML中快
通常状况下对于Android程序布局每每使用XML文件来编写,这样能够提升开发效率,可是考虑到代码的安全性以及执行效率,能够经过Java代码执行建立,虽然Android编译过的XML是二进制的,可是加载XML解析器的效率对于资源占用仍是比较大的,Java处理效率比XML快得多,可是对于一个复杂界面的编写,可能须要一些套嵌考虑,若是你思惟灵活的话,使用Java代码来布局你的Android应用程序是一个更好的方法。
重用系统资源:
1. 利用系统定义的id
好比咱们有一个定义ListView的xml文件,通常的,咱们会写相似下面的代码片断。
这里咱们定义了一个ListView,定义它的id是"@+id/mylist"。实际上,若是没有特别的需求,就能够利用系统定义的id,相似下面的样子。
2. 利用系统的图片资源
这样作的好处,一个是美工不须要重复的作一份已有的图片了,能够节约很多工时;另外一个是能保证咱们的应用程序的风格与系统一致。
3. 利用系统的字符串资源
若是使用系统的字符串,默认就已经支持多语言环境了。如上述代码,直接使用了@android:string/yes和@android:string/no,在简体中文环境下会显示“肯定”和“取消”,在英文环境下会显示“OK”和“Cancel”。
4. 利用系统的Style
假设布局文件中有一个TextView,用来显示窗口的标题,使用中等大小字体。可使用下面的代码片断来定义TextView的Style。
5. 利用系统的颜色定义
除了上述的各类系统资源之外,还可使用系统定义好的颜色。在项目中最经常使用的,就是透明色的使用。
除了上面介绍的之外还有不少其余Android系统自己自带的资源,它们在应用中均可以直接使用。具体的,能够进入android-sdk的相应文件夹中去查看。例如:能够进入$android-sdk$\platforms\android-8\data\res,里面的系统资源就尽收眼底了。
开发者须要花一些时间去熟悉这些资源,特别是图片资源和各类Style资源,这样在开发过程当中,能重用的尽可能重用,并且有时候使用系统提供的效果可能会更好。
其余小tips:
1. 分辨率适配-ldpi,-mdpi, -hdpi配置不一样精度资源,系统会根据设备自适应,包括drawable, layout,style等不一样资源。
2.尽可能使用dp(density independent pixel)开发,不用px(pixel)。
3.多用wrap_content, match_parent
4.永远不要使用AbsoluteLayout
5.使用9patch(经过~/tools/draw9patch.bat启动应用程序),png格式
6.将Acitivity中的Window的背景图设置为空。getWindow().setBackgroundDrawable(null);android的默认背景是否是为空。
7.View中设置缓存属性.setDrawingCache为true。
Desgin Review(设计检查):
Desgin Review主要侧重检查一下程序的设计是否合理,包括框架的设计,界面的设计,逻辑的设计(其实这些东西开发以前就应该想好了)。
框架设计:
是否认义了本身的Activity和fragment等经常使用控件的基类去避免进行重复的工做
是否有完善的异常处理机制,即便真的出现OOM也不会直接崩溃致使直接退出程序
界面设计:
1.在视图中加载你所须要的,而不是你所拥有。由于用户不可能同时看到全部东西。最典型的例子就是ListView中的滑动加载。
2.若是数据特别大,此时应该暗示用户去点击加载,而不是直接加载。
3.合理运用分屏,转屏等,它是个双刃剑,由于它便可以使程序更加美观功能更加完善,但也相应增长了资源开销。
逻辑设计:
避免子类直接去控制父类中内容,可使用监听等方式去解决