• ANDROID内存优化(大汇总——全)(转载)


    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!

    写在最前:

    本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总、挑选、简化后整理而成。

    所以我将本文定义为一个工具类的文章,如果你在ANDROID开发中遇到关于内存问题,或者马上要参加面试,或者就是单纯的学习或复习一下内存相关知识,都欢迎阅读。(本文最后我会尽量列出所参考的文章)。

    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方法来建议执行垃圾收集器。

    垃圾回收算法:

    1.引用计数(Reference Counting)
    比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。

    2.标记-清除(Mark-Sweep)
    此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。

    3.复制(Copying)
    此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不过出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间。

    4.标记-整理(Mark-Compact)
    此算法结合了 “标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象 “压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。

    5.增量收集(Incremental Collecting)
    实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。

    6.分代(Generational Collecting)
    基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。

    finalize():

    每一个对象都有一个finalize方法,这个方法是从Object类继承来的。

    当垃圾回收确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

    Java 技术允许使用finalize方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。一旦垃圾回收器准备好释放对象占用的空间,将首先调用其finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
    简单的说finalize方法是在垃圾收集器删除对象之前对这个对象调用的

    System.gc():

    我们可以调用System.gc方法,建议虚拟机进行垃圾回收工作(注意,是建议,但虚拟机会不会这样干,我们也无法预知!)

    下面来看一个例子来了解finalize()System.gc()的使用:

    [java] view plaincopyprint?

    1. public class TestGC { 
    2. public TestGC() {} 
    3. //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
    4. protected void finalize() { 
    5.         System.out.println("我已经被垃圾回收器回收了..."); 
    6.     } 
    7. public static void main(String [] args) { 
    8.         TestGC gc = new TestGC(); 
    9.         gc = null;   
    10. // 建议虚拟机进行垃圾回收工作
    11.         System.gc(); 
    12.     } 
    public class TestGC {
        public TestGC() {}
        
        //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
        protected void finalize() {
            System.out.println("我已经被垃圾回收器回收了...");
        }
        
        public static void main(String [] args) {
            TestGC gc = new TestGC();
            gc = null;  
            // 建议虚拟机进行垃圾回收工作
            System.gc();
        }
    }
    如上面的例子所示,大家可以猜猜重写的finalize方法会不会执行?

    答案是:不一定

    因为无论是设置gc的引用为null还是调用System.gc()方法都只是"建议"垃圾回收器进行垃圾回收,但是最终所有权还在垃圾回收器手中,它会不会进行回收我们无法预知!
    垃圾回收面试题:
    最后通过网上找到的3道面试题来结束垃圾回收的内容。

    面试题一:

    [java] view plaincopyprint?

    1. 1.fobj = new Object ( ) ;  
    2. 2.fobj. Method ( ) ;  
    3. 3.fobj = new Object ( ) ;  
    4. 4.fobj. Method ( ) ;  
    1.fobj = new Object ( ) ; 
    2.fobj. Method ( ) ; 
    3.fobj = new Object ( ) ; 
    4.fobj. Method ( ) ; 

    问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准? 
    答:第3行。因为第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中的fobj赋了null值。这种类型的题是最简单的。 
    面试题二:

    [java] view plaincopyprint?

    1. 1.Object sobj = new Object ( ) ;  
    2. 2.Object sobj = null ;  
    3. 3.Object sobj = new Object ( ) ;  
    4. 4.sobj = new Object ( ) ;  
    1.Object sobj = new Object ( ) ; 
    2.Object sobj = null ; 
    3.Object sobj = new Object ( ) ; 
    4.sobj = new Object ( ) ; 
    问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? 
    答:第2行和第4行。因为第2行为sobj赋值为null,所以在此第1行的sobj符合垃圾收集器的收集标准。而第4行相当于为sobj赋值为null,所以在此第3行的sobj也符合垃圾收集器的收集标准。 
    如果有一个对象的句柄a,且你把a作为某个构造器的参数,即 new Constructor ( a )的时候,即使你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才可以被垃圾收集器收集。 
    面试题三:

    [java] view plaincopyprint?

    1. 1.Object aobj = new Object ( ) ;  
    2. 2.Object bobj = new Object ( ) ;  
    3. 3.Object cobj = new Object ( ) ;  
    4. 4.aobj = bobj;  
    5. 5.aobj = cobj;  
    6. 6.cobj = null;  
    7. 7.aobj = null;  
    1.Object aobj = new Object ( ) ; 
    2.Object bobj = new Object ( ) ; 
    3.Object cobj = new Object ( ) ; 
    4.aobj = bobj; 
    5.aobj = cobj; 
    6.cobj = null; 
    7.aobj = null; 
    问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? 
    答:第4,7行。注意这类题型是认证考试中可能遇到的最难题型了。 
    行1-3:分别创建了Object类的三个对象:aobj,bobj,cobj
    行4:此时对象aobj的句柄指向bobj,原来aojb指向的对象已经没有任何引用或变量指向,这时,就符合回收标准。
    行5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 
    行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。 
    行7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然无法判断其是否符合收集标准。 
    总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集的标准只有两个: 
    1.给对象赋予了空值null,以下再没有调用过。 
    2.给对象赋予了新值,既重新分配了内存空间。 
    最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集。

    资源的回收:

    刚才讲了一堆理论的东西,下面来点实际能用上的,资源的回收:

    Thread(线程)回收:

    线程中涉及的任何东西GC都不能回收(Anything reachable by a thread cannot be GC'd ),所以线程很容易造成内存泄露。

    如下面代码所示:

    [java] view plaincopyprint?

    1. Thread t = new Thread() { 
    2. public void run() { 
    3. while (true) { 
    4. try { 
    5.                 Thread.sleep(1000); 
    6.                 System.out.println("thread is running..."); 
    7.             } catch (InterruptedException e) { 
    8.             } 
    9.         } 
    10.     } 
    11. }; 
    12. t.start(); 
    13. t = null; 
    14. System.gc(); 
    Thread t = new Thread() {
    	public void run() {
    		while (true) {
    			try {
    				Thread.sleep(1000);
    				System.out.println("thread is running...");
    			} catch (InterruptedException e) {
    			
    			}
    		}
    	}
    };
    t.start();
    t = null;
    System.gc();
    如上在线程t中每间隔一秒输出一段话,然后将线程设置为null并且调用System.gc方法。

    最后的结果是线程并不会被回收,它会一直的运行下去。

    因为运行中的线程是称之为垃圾回收根(GC Roots)对象的一种,不会被垃圾回收。当垃圾回收器判断一个对象是否可达,总是使用垃圾回收根对象作为参考点。

    Cursor(游标)回收:

    Cursor是Android查询数据后得到的一个管理数据集合的类,在使用结束以后。应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误提示。

    所以我们使用Cursor的方式一般如下:

    [java] view plaincopyprint?

    1. Cursor cursor = null; 
    2. try { 
    3.     cursor = mContext.getContentResolver().query(uri,null, null,null,null); 
    4. if(cursor != null) { 
    5.         cursor.moveToFirst(); 
    6. //do something
    7.     } 
    8. } catch (Exception e) { 
    9.     e.printStackTrace(); 
    10. } finally { 
    11. if (cursor != null) { 
    12.         cursor.close(); 
    13.     } 
            Cursor cursor = null;
            try {
                cursor = mContext.getContentResolver().query(uri,null, null,null,null);
                if(cursor != null) {
                    cursor.moveToFirst();
                    //do something
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
    有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。

    [java] view plaincopyprint?

    1. @Override
    2. protected void onDestroy() {         
    3. if (mAdapter != null && mAdapter.getCurosr() != null) {   
    4.         mAdapter.getCursor().close();   
    5.     }   
    6. super.onDestroy();    
    7. }   
    @Override  
    protected void onDestroy() {        
        if (mAdapter != null && mAdapter.getCurosr() != null) {  
            mAdapter.getCursor().close();  
        }  
        super.onDestroy();   
    }  

    Receiver(接收器)回收

    调用registerReceiver()后未调用unregisterReceiver().

    当我们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,一定要在Activity的生命周期内调用unregisterReceiver()方法取消注册 
    也就是说registerReceiver()和unregisterReceiver()方法一定要成对出现,通常我们可以重写Activity的onDestory()方法:

    [java] view plaincopyprint?

    1. @Override
    2. protected void onDestroy() {   
    3. this.unregisterReceiver(receiver);   
    4. super.onDestroy();   
    5. }   
    @Override  
    protected void onDestroy() {  
          this.unregisterReceiver(receiver);  
          super.onDestroy();  
    }  

    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:

    想要减少视图层级首先就需要知道视图层级,所以下面介绍一个SDK中自带的一个非常好用的工具hierarchyviewer。

    你可以在下面的地址找到它:your sdk pathsdk ools

    如上图大家可以看到,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文件,一般的,我们会写类似下面的代码片段。

    [html] view plaincopyprint?

    1. <ListView
    2. android:id="@+id/mylist"
    3. android:layout_width="fill_parent"
    4. android:layout_height="fill_parent"/>
    <ListView
        android:id="@+id/mylist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>

    这里我们定义了一个ListView,定义它的id是"@+id/mylist"。实际上,如果没有特别的需求,就可以利用系统定义的id,类似下面的样子。

    [html] view plaincopyprint?

    1. <ListView
    2. android:id="@android:id/list"
    3. android:layout_width="fill_parent"
    4. android:layout_height="fill_parent"/>
    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
    在xml文件中引用系统的id,只需要加上“@android:”前缀即可。如果是在Java代码中使用系统资源,和使用自己的资源基本上是一样的。不同的是,需要使用android.R类来使用系统的资源,而不是使用应用程序指定的R类。这里如果要获取ListView可以使用android.R.id.list来获取。

    2. 利用系统的图片资源

    这样做的好处,一个是美工不需要重复的做一份已有的图片了,可以节约不少工时;另一个是能保证我们的应用程序的风格与系统一致。

    3. 利用系统的字符串资源

    如果使用系统的字符串,默认就已经支持多语言环境了。如上述代码,直接使用了@android:string/yes和@android:string/no,在简体中文环境下会显示“确定”和“取消”,在英文环境下会显示“OK”和“Cancel”。

    4. 利用系统的Style

    假设布局文件中有一个TextView,用来显示窗口的标题,使用中等大小字体。可以使用下面的代码片段来定义TextView的Style。

    [html] view plaincopyprint?

    1. <TextView
    2. android:id="@+id/title"
    3. android:layout_width="wrap_content"
    4. android:layout_height="wrap_content"
    5. android:textAppearance="?android:attr/textAppearanceMedium" />
    <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    其中android:textAppearance="?android:attr/textAppearanceMedium"就是使用系统的style。需要注意的是,使用系统的style,需要在想要使用的资源前面加“?android:”作为前缀,而不是“@android:”。

    5. 利用系统的颜色定义

    除了上述的各种系统资源以外,还可以使用系统定义好的颜色。在项目中最常用的,就是透明色的使用。

    [html] view plaincopyprint?

    1. android:background ="@android:color/transparent"
    android:background ="@android:color/transparent"

    除了上面介绍的以外还有很多其他Android系统本身自带的资源,它们在应用中都可以直接使用。具体的,可以进入android-sdk的相应文件夹中去查看。例如:可以进入$android-sdk$platformsandroid-8data es,里面的系统资源就一览无余了。

    开发者需要花一些时间去熟悉这些资源,特别是图片资源和各种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.合理运用分屏,转屏等,它是个双刃剑,因为它即可以使程序更加美观功能更加完善,但也相应增加了资源开销。


    逻辑设计:

    避免子类直接去控制父类中内容,可以使用监听等方式去解决

    关于这三点由于我工作经验比较少,加上一时半会也想不出来多少,如果大家有建议希望可以留言,之后我给加进去。

    写在最后:

    到此ANDROID内存优化上、中、下三篇全部写完了。

    内存简介,Recoken(计算)请看ANDROID内存优化(大汇总——上)

    Reduce(减少),Reuse(重用) 请看:ANDROID内存优化(大汇总——中)

    Recycle(回收), Review(检查) 请看:ANDROID内存优化(大汇总——全)

    最初写这篇文章的原因是因为我拿到一个国外大牛演讲的PPT,我看过之后感觉写的非常好,于是想按照ppt的思路将其总结一下。结果到写的时候发现困难重重,因为内存本来就是很理论的东西,很多都是靠经验的。而我的经验几乎可以忽略,写的东西完全是网上各路文章的大汇总(所以大家千万不要叫我大神,我只是大神的搬运工。。。)

    虽然如此我觉得我总结和搜集的还算比较全面的,当然也有很多遗落也可能有很多错误,这个就希望大家一起帮着完善一下。

    最后我把这个PPT的原件附上,里面很多高级的东西我没看懂(比如那个5R中其实是没有Review的,原文是Reorder,由于这部分我看不懂而且找不到很好的资料只能自己换了一个Review),各路大神有兴趣可以看看,如果可以的话写出来分享一下。

    Putting Your App on a Memory Diet, Parts I and II_Murphy

    在这段时间里我正好也可以休息一下想想以后写点什么东西。像内存这种偏理论的东西我还是不要碰了,以后可能会多翻译一些国外大神的文章和自己做的一些小Demo吧。

    不知不觉Blog也写了快半年了,越来越觉得Blog这种分享精神的重要性,因为只有分享才能收获的更多!

    最后要谢谢那些关注,点赞和评论的网友们,这些真的是我能坚持下来的一个巨大动力!

    参考文章:

    Android内存优化http://blog.csdn.net/imain/article/details/8560986
    Android 内存优化http://blog.csdn.net/awangyunke/article/details/20380719
    关于android性能,内存优化http://www.cnblogs.com/zyw-205520/archive/2013/02/17/2914190.htm
    Java垃圾回收原理http://www.360doc.com/content/11/0911/15/18042_147476260.shtml
    JVM垃圾回收(GC)原理http://www.360doc.com/content/11/0911/16/18042_147492404.shtml

  • 相关阅读:
    python基础(一)
    python之使用__future__
    python中动态导入模块
    getResource()和getResourceAsStream以及路径问题
    〖转〗request.getparameter()和request.getAttribute()的区别
    IntelliJ IDEA 中module的dependencies是其它module时的注意事项
    IntelliJ IDEA 的Project structure说明
    c#中关于virtual,override和new的理解
    为什么在头文件中只能放声明不能放定义
    sql server 2008 中的架构(schame)理解
  • 原文地址:https://www.cnblogs.com/zjmsky/p/4790504.html
Copyright © 2020-2023  润新知