成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

性能優(yōu)化(三)看完這篇文章,至少解決 APP 中 90 % 的內(nèi)存異常問題

Elle / 2003人閱讀

摘要:不能滿足被回收的條件,盡管調(diào)用也還是不能得到回收這就造成了內(nèi)存泄漏。種解決單例中的內(nèi)存泄漏將引用置為銷毀監(jiān)聽使用弱引用將監(jiān)聽器放入弱引用中從弱引用中取出回調(diào)通過第七小點(diǎn)就能完美的解決單例中回調(diào)引起的內(nèi)存泄漏。

我們?yōu)槭裁匆獌?yōu)化內(nèi)存

在 Android 中我們寫的 .java 文件,最終會編譯成 .class 文件, class 又由類裝載器加載后,在 JVM 中會形成一份描述 class 結(jié)構(gòu)的元信息對象,通過該元信息對象可以知道 class 的結(jié)構(gòu)信息 (構(gòu)造函數(shù)、屬性、方法)等。JVM 會把描述類的數(shù)據(jù)從 class 文件加載到內(nèi)存,Java 有一個很好的管理內(nèi)存的機(jī)制,垃圾回收機(jī)制 GC 。為什么 Java 都給我們提供了垃圾回收機(jī)制,程序有時還會導(dǎo)致內(nèi)存泄漏,內(nèi)存溢出 OOM,甚至導(dǎo)致程序 Crash 。接下來我們就對實(shí)際開發(fā)中出現(xiàn)的這些內(nèi)存問題,來進(jìn)行優(yōu)化。

JAVA 虛擬機(jī)

我們先來大概了解一下 Java 虛擬機(jī)里面運(yùn)行時的數(shù)據(jù)區(qū)域有哪些,如果想深入了解 Java 虛擬機(jī) 建議可以購買<<深入理解 Java 虛擬機(jī)>> 或者直接點(diǎn)擊我這里的 PDF 版本 密碼: jmnf

線程獨(dú)占區(qū)

程序計數(shù)器

相當(dāng)于一個執(zhí)行代碼的指示器,用來確認(rèn)下一行執(zhí)行的地址

每個線程都有一個

沒有 OOM 的區(qū)

虛擬機(jī)棧

我們平時說的棧就是這塊區(qū)域

java 虛擬機(jī)規(guī)范中定義了 OutOfMemeory , stackoverflow 異常

本地方法棧

java 虛擬機(jī)規(guī)范中定義了 OutOfMemory ,stackoverflow 異常

注意

在 hotspotVM 中把虛擬機(jī)棧和本地方法棧合為了一個棧區(qū)

線程共享區(qū)

方法區(qū)

ClassLoader 加載類信息

常量、靜態(tài)變量

編譯后的代碼

會出現(xiàn) OOM

運(yùn)行時常量池

public static final

符號引用類、接口全名、方法名

java 堆 (本次需要優(yōu)化的地方)

虛擬機(jī)能管理的最大的一塊內(nèi)存 GC 主戰(zhàn)場

會出現(xiàn) OOM

對象實(shí)例

數(shù)據(jù)的內(nèi)容

JAVA GC 如何確定內(nèi)存回收

隨著程序的運(yùn)行,內(nèi)存中的實(shí)例對象、變量等占據(jù)的內(nèi)存越來越多,如果不及時進(jìn)行回收,會降低程序運(yùn)行效率,甚至引發(fā)系統(tǒng)異常。

目前虛擬機(jī)基本都是采用可達(dá)性分析算法,為什么不采用引用計數(shù)算法呢?下面就說說引用計數(shù)法是如果統(tǒng)計所有對象的引用計數(shù)的,再對比可達(dá)性分析算法是如何解決引用計數(shù)算法的不足。下面就來看下這 2 個算法:

引用計數(shù)算法

每個對象有一個引用計數(shù)器,當(dāng)對象被引用一次則計數(shù)器加一,當(dāng)對象引用一次失效一次則計數(shù)器減一,對于計數(shù)器為 0 的時候就意味著是垃圾了,可以被 GC 回收。

下面通過一段代碼來實(shí)際看下

public class GCTest {
    private Object instace = null;

    public static void onGCtest() {
        //step 1
        GCTest gcTest1 = new GCTest();
        //step 2
        GCTest gcTest2 = new GCTest();
        //step 3
        gcTest1.instace = gcTest2;
        //step 4
        gcTest2.instace = gcTest1;
        //step 5
        gcTest1 = null;
        //step 6
        gcTest2 = null;

    }

    public static void main(String[] arg) {
        onGCtest();
    }
}

分析代碼

//step 1 gcTest1 引用 + 1 = 1
//step 2 gcTest2 引用 + 1 = 1
//step 3 gcTest1 引用 + 1 = 2
//step 4 gcTest2 引用 + 1 = 2
//step 5 gcTest1 引用 - 1 = 1
//step 6 gcTest2 引用 - 1 = 1

很明顯現(xiàn)在 2 個對象都不能用了都為 null 了,但是 GC 確不能回收它們,因?yàn)樗鼈儽旧淼囊糜嫈?shù)不為 0 。不能滿足被回收的條件,盡管調(diào)用 System.gc() 也還是不能得到回收, 這就造成了 內(nèi)存泄漏 。當(dāng)然,現(xiàn)在虛擬機(jī)基本上都不采用此方式。

可達(dá)性分析算法

從 GC Roots 作為起點(diǎn)開始搜索,那么整個連通圖中額對象邊都是活對象,對于 GC Roots 無法到達(dá)的對象便成了垃圾回收的對象,隨時可能被 GC 回收。

可以作為 GC Roots 的對象

虛擬機(jī)棧正在運(yùn)行使用的引用

靜態(tài)屬性 常量

JNI 引用的對象

GC 是需要 2 次掃描才回收對象,所以我們可以使用 finalize 去救活丟失的引用

 @Override
    protected void finalize() throws Throwable {
        super.finalize();
        instace = this;
    }

到了這里,相信大家已經(jīng)能夠弄明白這 2 個算法的區(qū)別了吧?反正對于對象之間循環(huán)引用的情況,引用計數(shù)算法無法回收這 2 個對象,而可達(dá)性是從 GC Roots 開始搜索,所以能夠正確的回收。

不同引用類型的回收狀態(tài)

強(qiáng)引用

Object strongReference = new Object()

如果一個對象具有強(qiáng)引用,那垃圾回收器絕不會回收它,當(dāng)內(nèi)存空間不足, Java 虛擬機(jī)寧愿拋出 OOM 錯誤,使程序異常 Crash ,也不會靠隨意回收具有強(qiáng)引用的對象來解決內(nèi)存不足的問題.如果強(qiáng)引用對象不再使用時,需要弱化從而使 GC 能夠回收,需要:

strongReference = null; //等 GC 來回收

還有一種情況,如果:

public void onStrongReference(){
    Object strongReference = new Object()
}

在 onStrongReference() 內(nèi)部有一個強(qiáng)引用,這個引用保存在 java 棧 中,而真正的引用內(nèi)容 (Object)保存在 java 堆中。當(dāng)這個方法運(yùn)行完成后,就會退出方法棧,則引用對象的引用數(shù)為 0 ,這個對象會被回收。

但是如果 mStrongReference 引用是全局時,就需要在不用這個對象時賦值為 null ,因?yàn)?強(qiáng)引用 不會被 GC 回收。

軟引用 (SoftReference)

如果一個對象只具有軟引用,則內(nèi)存空間足夠,垃圾回收器就不會回收它;如果內(nèi)存空間不足了,就會回收這些對象的內(nèi)存,只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存。

軟引用可以和一個引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對象被垃圾回收器回收, java 虛擬機(jī)就會把這個軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

注意: 軟引用對象是在 jvm 內(nèi)存不夠的時候才會被回收,我們調(diào)用 System.gc() 方法只是起通知作用, JVM 什么時候掃描回收對象是 JVM 自己的狀態(tài)決定的。就算掃描到了 str 這個對象也不會回收,只有內(nèi)存不足才會回收。

弱引用 (WeakReference)

弱引用與軟引用的區(qū)別在于: 只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。不過由于垃圾回收器是一個優(yōu)先級很低的線程,因此不一定會很快發(fā)現(xiàn)那些只具有弱引用的對象。

弱引用可以和一個引用隊(duì)列聯(lián)合使用,如果弱引用所引用的對象被垃圾回收,Java 虛擬機(jī)就會把這個弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

可見 weakReference 對象的生命周期基本由 GC 決定,一旦 GC 線程發(fā)現(xiàn)了弱引用就標(biāo)記下來,第二次掃描到就直接回收了。

注意這里的 referenceQueuee 是裝的被回收的對象。

虛引用 (PhantomReference)

    @Test
    public void onPhantomReference()throws InterruptedException{
        String str = new String("123456");
        ReferenceQueue queue = new ReferenceQueue();
        // 創(chuàng)建虛引用,要求必須與一個引用隊(duì)列關(guān)聯(lián)
        PhantomReference pr = new PhantomReference(str, queue);
        System.out.println("PhantomReference:" + pr.get());
        System.out.printf("ReferenceQueue:" + queue.poll());
    }

虛引用顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。

虛引用主要用來跟蹤對象被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區(qū)別在于: 虛引用必須和引用隊(duì)列 (ReferenceQueue) 聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個對象時,如果發(fā)現(xiàn)它還有虛引用,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。

總結(jié)

引用類型 調(diào)用方式 GC 是否內(nèi)存泄漏
強(qiáng)引用 直接調(diào)用 不回收
軟引用 .get() 視內(nèi)存情況回收
弱引用 .get() 回收 不可能
虛引用 null 任何時候都可能被回收,相當(dāng)于沒有引用一樣
分析內(nèi)存常用工具

工具很多,掌握原理方法,工具隨意挑選使用。

top/procrank meinfo Procstats DDMS MAT Finder - Activity LeakCanary LeakInspector 內(nèi)存泄漏

產(chǎn)生的原因: 一個長生命周期的對象持有一個短生命周期對象的引用,通俗點(diǎn)講就是該回收的對象,因?yàn)橐脝栴}沒有被回收,最終會產(chǎn)生 OOM。

下面我們來利用 Profile 來檢查項(xiàng)目是否有內(nèi)存泄漏

怎么利用 profile 來查看項(xiàng)目中是否有內(nèi)存泄漏

    在 AS 中項(xiàng)目以 profile 運(yùn)行

    在 MEMORY 界面中選擇要分析的一段內(nèi)存,右鍵 export

    Allocations: 動態(tài)分配對象個數(shù)

    Deallocation: 解除分配的對象個數(shù)

    Total count: 對象的總數(shù)

    Shalow Size: 對象本身占用的內(nèi)存大小

    Retained Size: GC 回收能收走的內(nèi)存大小

    轉(zhuǎn)換 profile 文件格式

    將 export 導(dǎo)出的 dprof 文件轉(zhuǎn)換為 Mat 的 dprof 文件

    cd /d 進(jìn)入到 Android sdk/platform-tools/hprof-conv.exe

    //轉(zhuǎn)換命令 hprof-conv -z src des
    D:AndroidAndroidDeveloper-sdkandroid-sdk-windowsplatform-tools>hprof-conv -z D:	emp_	emp_6.hprof D:	emp_memory6.hprod
    

    下載 Mat 工具

    打開 MemoryAnalyzer.exe 點(diǎn)擊左上角 File 菜單中的 Open Heap Dupm

    查看內(nèi)存泄漏中的 GC Roots 強(qiáng)引用

    這里我們得知是一個 ilsLoginListener 引用了 LoginView,我們來看下代碼最后怎么解決的。

    代碼中我們找到了 LoginView 這個類,發(fā)現(xiàn)是一個單例中的回調(diào)引起的內(nèi)存泄漏,下面怎么解決勒,請看第七小點(diǎn)。

    2種解決單例中的內(nèi)存泄漏

      將引用置為 null

      /**
           * 銷毀監(jiān)聽
           */
          public void unRemoveRegisterListener(){
              mMessageController.unBindListener();
          }
          public void unBindListener(){
              if (listener != null){
                  listener = null;
              }
          }
      

      使用弱引用

      //將監(jiān)聽器放入弱引用中
      WeakReference listenerWeakReference = new WeakReference<>(listener);
      
      //從弱引用中取出回調(diào)
      listenerWeakReference.get();
      

    通過第七小點(diǎn)就能完美的解決單例中回調(diào)引起的內(nèi)存泄漏。

Android 中常見的內(nèi)存泄漏經(jīng)典案例及解決方法

    單例

    示例 :

    public class AppManager {
    
        private static AppManager sInstance;
        private CallBack mCallBack;
        private Context mContext;
    
        private AppManager(Context context) {
            this.mContext = context;
        }
    
        public static AppManager getInstance(Context context) {
            if (sInstance == null) {
                sInstance = new AppManager(context);
            }
            return sInstance;
        }
        
        public void addCallBack(CallBack call){
            mCallBack = call;
        }
    }
    

      通過上面的單列,如果 context 傳入的是 Activity , Service 的 this,那么就會導(dǎo)致內(nèi)存泄漏。

      以 Activity 為例,當(dāng) Activity 調(diào)用 getInstance 傳入 this ,那么 sInstance 就會持有 Activity 的引用,當(dāng) Activity 需要關(guān)閉的時候需要 回收的時候,發(fā)現(xiàn) sInstance 還持有 沒有用的 Activity 引用,導(dǎo)致 Activity 無法被 GC 回收,就會造成內(nèi)存泄漏

      addCallBack(CallBack call) 這樣寫看起來是沒有毛病的。但是當(dāng)這樣調(diào)用在看一下勒。

      //在 Activity 中實(shí)現(xiàn)單例的回調(diào)
      AppManager.getInstance(getAppcationContext()).addCallBack(new CallBack(){
          @Override
          public void onStart(){
              
          }
      });
      

      這里的 new CallBack() 匿名內(nèi)部類 默認(rèn)持有外部的引用,造成 CallBack 釋放不了,那么怎么解決了,請看下面解決方法

    解決方法:

      getInstance(Context context) context 都傳入 Appcation 級別的 Context,或者實(shí)在是需要傳入 Activity 的引用就用 WeakReference 這種形式。

      匿名內(nèi)部類建議大家多帶帶寫一個文件或者

      public void addCallBack(CallBack call){
              WeakReference mCallBack= new WeakReference(call);
          }
      

    Handler

    示例:

    //在 Activity 中實(shí)現(xiàn) Handler
    class MyHandler extends Handler{
        private Activity m;
        public MyHandler(Activity activity){
            m=activity;
        }
    
    //    class.....
    }
    

    這里的 MyHandler 持有 activity 的引用,當(dāng) Activity 銷毀的時候,導(dǎo)致 GC 不會回收造成 內(nèi)存泄漏。

    解決方法:

    1.使用靜態(tài)內(nèi)部類 + 弱引用
    2.在 Activity onDestoty() 中處理  removeCallbacksAndMessages() 
        @Override
        protected void onDestroy() {
            super.onDestroy();
        if(null != handler){
              handler.removeCallbacksAndMessages(null);
              handler = null;
        }
     }
    

    靜態(tài)變量

    示例:

    public class MainActivity extends AppCompatActivity {
    
        private static Police sPolice;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sPolice != null) {
                sPolice = new Police(this);
            }
        }
    }
    
    class Police {
        public Police(Activity activity) {
        }
    }
    

    這里 Police 持有 activity 的引用,會造成 activity 得不到釋放,導(dǎo)致內(nèi)存泄漏。

    解決方法:

    //1. sPolice 在 onDestory()中 sPolice = null;
    //2. 在 Police 構(gòu)造函數(shù)中 將強(qiáng)引用 to 弱引用;
    

    非靜態(tài)內(nèi)部類

    參考 第二點(diǎn) Handler 的處理方式

    匿名內(nèi)部類

    示例:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
          	new Thread(){
                 @Override
                 public void run() {
                    super.run();
                            }
                        };
        }
    }
    

    很多初學(xué)者都會像上面這樣新建線程和異步任務(wù),殊不知這樣的寫法非常地不友好,這種方式新建的子線程ThreadAsyncTask都是匿名內(nèi)部類對象,默認(rèn)就隱式的持有外部Activity的引用,導(dǎo)致Activity內(nèi)存泄露。

    解決方法:

    //靜態(tài)內(nèi)部類 + 弱引用
    //多帶帶寫一個文件 + onDestory  = null;
    

    未取消注冊或回調(diào)

    示例:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            registerReceiver(mReceiver, new IntentFilter());
        }
    
        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // TODO ------
            }
        };
    }
    

    在注冊觀察則模式的時候,如果不及時取消也會造成內(nèi)存泄露。比如使用Retrofit + RxJava注冊網(wǎng)絡(luò)請求的觀察者回調(diào),同樣作為匿名內(nèi)部類持有外部引用,所以需要記得在不用或者銷毀的時候取消注冊。

    解決方法:

    //Activity 中實(shí)現(xiàn) onDestory()反注冊廣播得到釋放
        @Override
        protected void onDestroy() {
            super.onDestroy();
            this.unregisterReceiver(mReceiver);
        }
    

    定時任務(wù)

    示例:

    public class MainActivity extends AppCompatActivity {
    
        /**模擬計數(shù)*/
        private int mCount = 1;
        private Timer mTimer;
        private TimerTask mTimerTask;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
            mTimer.schedule(mTimerTask, 1000, 1000);
        }
    
        private void init() {
            mTimer = new Timer();
            mTimerTask = new TimerTask() {
                @Override
                public void run() {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            addCount();
                        }
                    });
                }
            };
        }
    
        private void addCount() {
          mCount += 1;
        }
    }
    

    當(dāng)我們Activity銷毀的時,有可能Timer還在繼續(xù)等待執(zhí)行TimerTask,它持有Activity 的引用不能被 GC 回收,因此當(dāng)我們 Activity 銷毀的時候要立即cancelTimerTimerTask,以避免發(fā)生內(nèi)存泄漏。

    解決方法:

    //當(dāng) Activity 關(guān)閉的時候,停止一切正在進(jìn)行中的定時任務(wù),避免造成內(nèi)存泄漏。
        private void stopTimer() {
            if (mTimer != null) {
                mTimer.cancel();
                mTimer = null;
            }
            if (mTimerTask != null) {
                mTimerTask.cancel();
                mTimerTask = null;
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            stopTimer();
        }
    

    資源未關(guān)閉

    示例:

    ArrayList,HashMap,IO,File,SqLite,Cursor 等資源用完一定要記得 clear remove 等關(guān)閉一系列對資源的操作。
    

    解決方法:

    用完即刻銷毀
    

    屬性動畫

    示例:

    動畫同樣是一個耗時任務(wù),比如在 Activity 中啟動了屬性動畫 (ObjectAnimator) ,但是在銷毀的時候,沒有調(diào)用 cancle 方法,雖然我們看不到動畫了,但是這個動畫依然會不斷地播放下去,動畫引用所在的控件,所在的控件引用 Activity ,這就造成 Activity 無法正常釋放。因此同樣要在Activity 銷毀的時候 cancel 掉屬性動畫,避免發(fā)生內(nèi)存泄漏。
    

    解決方法:

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //當(dāng)關(guān)閉 Activity 的時候記得關(guān)閉動畫的操作
        mAnimator.cancel();
    }
    

    Android 源碼或者第三方 SDK

    示例:

    //如果在開發(fā)調(diào)試中遇見 Android 源碼或者 第三方 SDK 持有了我們當(dāng)前的 Activity 或者其它類,那么現(xiàn)在怎么辦了。
    

    解決方法:

    //當(dāng)前是通過 Java 中的反射找到某個類或者成員,來進(jìn)行手動 = null 的操作。
    

內(nèi)存抖動 什么是內(nèi)存抖動

內(nèi)存頻繁的分配與回收,(分配速度大于回收速度時) 最終產(chǎn)生 OOM 。

也許下面的錄屏更能解釋什么是內(nèi)存抖動

可以看出當(dāng)我點(diǎn)擊了一下 Button 內(nèi)存就頻繁的創(chuàng)建并回收(注意看垃圾桶)。

那么我們找出代碼中具體那一塊出現(xiàn)問題了勒,請看下面一段錄屏

  
mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                imPrettySureSortingIsFree();
            }
        });

/**
     * 排序后打印二維數(shù)組,一行行打印
     */
    public void imPrettySureSortingIsFree() {
        int dimension = 300;
        int[][] lotsOfInts = new int[dimension][dimension];
        Random randomGenerator = new Random();
        for (int i = 0; i < lotsOfInts.length; i++) {
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                lotsOfInts[i][j] = randomGenerator.nextInt();
            }
        }
        


        for (int i = 0; i < lotsOfInts.length; i++) {
            String rowAsStr = "";
            //排序
            int[] sorted = getSorted(lotsOfInts[i]);
            //拼接打印
            for (int j = 0; j < lotsOfInts[i].length; j++) {
                rowAsStr += sorted[j];
                if (j < (lotsOfInts[i].length - 1)) {
                    rowAsStr += ", ";
                }
            }
            Log.i("ricky", "Row " + i + ": " + rowAsStr);
        }
    }

最后我們之后是 onClick 中的 imPrettySureSortingIsFree() 函數(shù)里面的 rowAsStr += sorted[j]; 字符串拼接造成的 內(nèi)存抖動 ,因?yàn)槊看纹唇右粋€ String 都會申請一塊新的堆內(nèi)存,那么怎么解決這個頻繁開辟內(nèi)存的問題了。其實(shí)在 Java 中有 2 個更好的 API 對 String 的操作很友好,相信應(yīng)該有人猜到了吧。沒錯就是將 此處的 String 換成 StringBuffer 或者 StringBuilder,就能很完美的解決字符串拼接造成的內(nèi)存抖動問題。

修改后

        /**
         * 打印二維數(shù)組,一行行打印
         */
        public void imPrettySureSortingIsFree() {
            int dimension = 300;
            int[][] lotsOfInts = new int[dimension][dimension];
            Random randomGenerator = new Random();
            for(int i = 0; i < lotsOfInts.length; i++) {
                for (int j = 0; j < lotsOfInts[i].length; j++) {
                    lotsOfInts[i][j] = randomGenerator.nextInt();
                }
            }

            // 使用StringBuilder完成輸出,我們只需要創(chuàng)建一個字符串即可,				不需要浪費(fèi)過多的內(nèi)存
            StringBuilder sb = new StringBuilder();
            String rowAsStr = "";
            for(int i = 0; i < lotsOfInts.length; i++) {
                // 清除上一行
                sb.delete(0, rowAsStr.length());
                //排序
                int[] sorted = getSorted(lotsOfInts[i]);
                //拼接打印
                for (int j = 0; j < lotsOfInts[i].length; j++) {
                    sb.append(sorted[j]);
                    if(j < (lotsOfInts[i].length - 1)){
                        sb.append(", ");
                    }
                }
                rowAsStr = sb.toString();
                Log.i("jason", "Row " + i + ": " + rowAsStr);
            }
        }

這里可以看見沒有垃圾桶出現(xiàn),說明內(nèi)存抖動解決了。

注意: 實(shí)際開發(fā)中如果在 LogCat 中發(fā)現(xiàn)有這些 Log 說明也發(fā)生了 內(nèi)存抖動 (Log 中出現(xiàn) concurrent copying GC freed ....)

回收算法

ps:我覺得這個只是為了應(yīng)付面試,那么可以參考這里,我也只了解概念這里就不用在多寫了,點(diǎn)擊看這個帖子吧

也可以參考掘金的這一篇 GC 回收算法

標(biāo)記清除算法 Mark-Sweep 復(fù)制算法 Copying 標(biāo)記壓縮算法 Mark-Compact 分代收集算法 總結(jié) (只要養(yǎng)成這樣的習(xí)慣,至少可以避免 90 % 以上不會造成內(nèi)存異常)

    數(shù)據(jù)類型: 不要使用比需求更占用空間的基本數(shù)據(jù)類型

    循環(huán)盡量用 foreach ,少用 iterator, 自動裝箱也盡量少用

    數(shù)據(jù)結(jié)構(gòu)與算法的解度處理 (數(shù)組,鏈表,棧樹,樹,圖)

    數(shù)據(jù)量千級以內(nèi)可以使用 Sparse 數(shù)組 (Key為整數(shù)),ArrayMap (Key 為對象) 雖然性能不如 HashMap ,但節(jié)約內(nèi)存。

    枚舉優(yōu)化

    缺點(diǎn):

    每一個枚舉值都是一個單例對象,在使用它時會增加額外的內(nèi)存消耗,所以枚舉相比與 Integer 和 String 會占用更多的內(nèi)存

    較多的使用 Enum 會增加 DEX 文件的大小,會造成運(yùn)行時更多的 IO 開銷,使我們的應(yīng)用需要更多的空間

    特別是分 Dex 多的大型 APP,枚舉的初始化很容易導(dǎo)致 ANR

    優(yōu)化后的代碼:可以直接限定傳入的參數(shù)個數(shù)

    public class SHAPE {
        public static final int TYPE_0=0;
        public static final int TYPE_1=1;
        public static final int TYPE_2=2;
        public static final int TYPE_3=3;
    
    
        @IntDef(flag=true,value={TYPE_0,TYPE_1,TYPE_2,TYPE_3})
        @Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})
        @Retention(RetentionPolicy.SOURCE)
        public @interface Model{
    
        }
    
        private @Model int value=TYPE_0;
        public void setShape(@Model int value){
            this.value=value;
        }
        @Model
        public int getShape(){
            return this.value;
        }
    }
    
    

    static , static final 的問題

    static 會由編譯器調(diào)用 clinit 方法進(jìn)行初始化

    static final 不需要進(jìn)行初始化工作,打包在 dex 文件中可以直接調(diào)用,并不會在類初始化申請內(nèi)存

    基本數(shù)據(jù)類型的成員,可以全寫成 static final

    字符串的拼接盡量少用 +=

    重復(fù)申請內(nèi)存問題

    同一個方法多次調(diào)用,如遞歸函數(shù) ,回調(diào)函數(shù)中 new 對象

    不要在 onMeause() onLayout() ,onDraw() 中去刷新UI(requestLayout)

    避免 GC 回收將來要重新使用的對象 (內(nèi)存設(shè)計模式對象池 + LRU 算法)

    Activity 組件泄漏

    非業(yè)務(wù)需要不要把 activity 的上下文做參數(shù)傳遞,可以傳遞 application 的上下文

    非靜態(tài)內(nèi)部類和匿名內(nèi)部內(nèi)會持有 activity 引用(靜態(tài)內(nèi)部類 或者 多帶帶寫文件)

    單例模式中回調(diào)持有 activity 引用(弱引用)

    handler.postDelayed() 問題

    如果開啟的線程需要傳入?yún)?shù),用弱引接收可解決問題

    handler 記得清除 removeCallbacksAndMessages(null)

    Service 耗時操作盡量使用 IntentService,而不是 Service

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/7402.html

相關(guān)文章

  • 2017文章總結(jié)

    摘要:歡迎來我的個人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...

    dailybird 評論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來我的個人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...

    hellowoody 評論0 收藏0
  • 2017文章總結(jié)

    摘要:歡迎來我的個人站點(diǎn)性能優(yōu)化其他優(yōu)化瀏覽器關(guān)鍵渲染路徑開啟性能優(yōu)化之旅高性能滾動及頁面渲染優(yōu)化理論寫法對壓縮率的影響唯快不破應(yīng)用的個優(yōu)化步驟進(jìn)階鵝廠大神用直出實(shí)現(xiàn)網(wǎng)頁瞬開緩存網(wǎng)頁性能管理詳解寫給后端程序員的緩存原理介紹年底補(bǔ)課緩存機(jī)制優(yōu)化動 歡迎來我的個人站點(diǎn) 性能優(yōu)化 其他 優(yōu)化瀏覽器關(guān)鍵渲染路徑 - 開啟性能優(yōu)化之旅 高性能滾動 scroll 及頁面渲染優(yōu)化 理論 | HTML寫法...

    wwolf 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<