乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      【Android性能優(yōu)化】內(nèi)存泄露和內(nèi)存溢出(OOM)的引發(fā)原因及優(yōu)化方案

       jiffes 2017-08-04

      轉(zhuǎn)載請注明原文地址:http://blog.csdn.net/mxm691292118/article/details/51020023

      我把Android重難點和讀書筆記都整理在github上:https://github.com/miomin/AndroidDifficulty

      如果你覺得對你有幫助的話,希望可以star/follow一下喲,我會持續(xù)保持更新。


      一、內(nèi)存泄露

      • 垃圾回收器無法回收原本應(yīng)該被回收的對象,這個對象就引發(fā)了內(nèi)存泄露。
      • 內(nèi)存泄露的危害:
        • (1)過多的內(nèi)存泄露最終會導(dǎo)致內(nèi)存溢出(OOM)
        • (2)內(nèi)存泄露導(dǎo)致可用內(nèi)存不足,會觸發(fā)頻繁GC,不管是Android2.2以前的單線程GC還是現(xiàn)在的CMS和G1,都有一部分的操作會導(dǎo)致用戶線程停止(就是所謂的Stop the world),從而導(dǎo)致UI卡頓。

      二、內(nèi)存溢出(OOM)

      • android為每個進(jìn)程設(shè)置Dalvik Heap Size閾值,這個閾值在不同的設(shè)備上會因為RAM大小不同而各有差異。如果APP想要分配的內(nèi)存超過這個閾值,就會發(fā)生OOM。

      • ActivityManager.getMemoryClass()可以查詢當(dāng)前APP的Heap Size閾值,單位是MB。

      • 在3.x以前,Bitmap分配在Native heap中,而在4.x之后,Bitmap分配在Dalvik或ART的Java heap中。

      • Android 2.x系統(tǒng),當(dāng)dalvik allocated + native allocated + 新分配的大小 >= dalvik heap 最大值時候就會發(fā)生OOM,也就是說在2.x系統(tǒng)中,考慮native heap對每個進(jìn)程的內(nèi)存限制。

      • Android 4.x系統(tǒng),廢除了native的計數(shù)器,類似bitmap的分配改到dalvik的java heap中申請,只要allocated + 新分配的內(nèi)存 >= dalvik heap 最大值的時候就會發(fā)生OOM(art運行環(huán)境的統(tǒng)計規(guī)則還是和dalvik保持一致),也就是說在4.x系統(tǒng)中,不考慮native heap對每個進(jìn)程的內(nèi)存限制,native heap只會收到本機總內(nèi)存(包括RAM以及SWAP區(qū)或分頁文件)的限制。

      三、如何避免內(nèi)存泄漏

      參考在MDCC 2015中國移動開發(fā)者大會上胡凱前輩的講述,整理總結(jié)。

      1、使用輕量的數(shù)據(jù)結(jié)構(gòu)

      • 使用ArrayMap/SparseArray來代替HashMap,ArrayMap/SparseArray是專門為移動設(shè)備設(shè)計的高效的數(shù)據(jù)結(jié)構(gòu)。

      • HashMap實現(xiàn)原理

        • HashMap內(nèi)部使用一個默認(rèn)容量為16的數(shù)組來存儲數(shù)據(jù),采用拉鏈法解決hash沖突(數(shù)組+鏈表),如下圖:
          這里寫圖片描述

        • Entry存儲的內(nèi)容有key、value、hash值、next指針,通過計算hash(key)%len找到Entry在數(shù)組中的位置。

        • 缺點:(1)就算沒有數(shù)據(jù),也需要分配默認(rèn)16個元素的數(shù)組(2)一旦數(shù)據(jù)量達(dá)到Hashmap限定容量的75%,就將按兩倍擴容
      • SparseArray

        • 支持int類型,避免自動裝箱,但是也只支持int類型的key
        • 內(nèi)部通過兩個數(shù)組來進(jìn)行數(shù)據(jù)存儲的,一個存儲key,另外一個存儲value
        • 因為key是int,在查找時,采用二分查找,效率高,SparseArray存儲的元素都是按元素的key值從小到大排列好的。 (Hashmap通過遍歷Entry數(shù)組來獲取對象)
        • 默認(rèn)初始size為0,每次增加元素,size++
        • SparseArray中put方法的源碼如下:
      • ArrayMap

        • 跟SparseArray一樣,內(nèi)部兩個數(shù)組,但是第一個存key的hash值,一個存value,對象按照key的hash值排序,二分查找也是按照hash
        • 查找index時,傳入key,計算出hash,通過二分查找hash數(shù)組,確定index

      2、不要使用Enum

      3、大胖子Bitmap的處理

      • Bitmap壓縮
      • Lru機制處理Bitmap(下一節(jié)博客會詳細(xì)講解),也可以使用那些有名的圖片緩存框架。

      4、不要使用String進(jìn)行字符串拼接

      • 嚴(yán)格的講,String拼接只能歸結(jié)到內(nèi)存抖動中,因為產(chǎn)生的String副本能夠被GC,不會造成內(nèi)存泄露。

      • 頻繁的字符串拼接,使用StringBuffer或者StringBuilder代替String,可以在一定程度上避免OOM和內(nèi)存抖動。

      5、非靜態(tài)內(nèi)部類內(nèi)存泄露

      • 在Activity中創(chuàng)建非靜態(tài)內(nèi)部類,非靜態(tài)內(nèi)部類會持有Activity的隱式引用,若內(nèi)部類生命周期長于Activity,會導(dǎo)致Activity實例無法被回收。(屏幕旋轉(zhuǎn)后會重新創(chuàng)建Activity實例,如果內(nèi)部類持有引用,將會導(dǎo)致旋轉(zhuǎn)前的實例無法被回收)。

      • 解決方案:如果一定要使用內(nèi)部類,就改用static內(nèi)部類,在內(nèi)部類中通過WeakReference的方式引用外界資源。

      • 正確的代碼示例:

      static class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
      
              private String url;
              private WeakReference<PhotoAdapter> photoAdapter;
      
              public ImageDownloadTask(PhotoAdapter photoAdapter) {
                  this.photoAdapter = new WeakReference<PhotoAdapter>(photoAdapter);
              }
      
              @Override
              protected Bitmap doInBackground(String... params) {
                  //在后臺開始下載圖片
                  url = params[0];
                  Bitmap bitmap = photoAdapter.get().loadBitmap(url);
                  if (bitmap != null) {
                      //把下載好的圖片放入LruCache中
                      String key = MD5Tools.decodeString(url);
                      photoAdapter.get().put(key, bitmap);
                  }
                  return bitmap;
              }
      
              @Override
              protected void onPostExecute(Bitmap bitmap) {
                  super.onPostExecute(bitmap);
                  //把下載好的圖片顯示出來
                  ImageView mImageView = (ImageView) photoAdapter.get().mGridView.get().findViewWithTag(MD5Tools.decodeString(url));
                  if (mImageView != null && bitmap != null) {
                      mImageView.setImageBitmap(bitmap);
                      photoAdapter.get().mDownloadTaskList.remove(this);//把下載好的任務(wù)移除
                  }
              }
          }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33

      6、匿名內(nèi)部類內(nèi)存泄漏

      • 跟非靜態(tài)內(nèi)部類一樣,匿名內(nèi)部類也會持有外部類的隱式引用,比較常見的情況有,耗時Handler,耗時Thread,都會造成內(nèi)存泄漏,解決方式也是static+WeakReference,下面給出正確寫法。

      • Handler的正確寫法:

      private static class MyHandler extends Handler {
      
          private final WeakReference<Context> context;
      
          private MyHandler(Context context) {
              this.context = new WeakReference<Context>(context);
          }
      
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
      
              }
          }
      }
      
      private final MyHandler mHandler = new MyHandler(this);
      
      private static final Runnable sRunnable = new Runnable() {
          @Override
          public void run() {
      
          }
      };
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_home);
          //  發(fā)送一個10分鐘后執(zhí)行的一個消息
          mHandler.postDelayed(sRunnable, 600000);
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • Thread的正確寫法:
      private static class MyThread extends Thread {
      
          @Override
          public void run() {
              while (true) {
                  // TODO 耗時任務(wù)
              }
          }
      }
      
      new MyThread().start();
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      7、Context持有導(dǎo)致內(nèi)存泄漏

      • Activity Context被傳遞到其他實例中,這可能導(dǎo)致自身被引用而發(fā)生泄漏。
      • 解決:對于大部分非必須使用Activity Context的情況(創(chuàng)建Dialog的Context必須是Activity Context),應(yīng)該使用Application Context。

      8、記得注銷監(jiān)聽器

      • 注冊監(jiān)聽器的時候會add Listener,不要忘記在不需要的時候remove掉Listener。

      9、資源文件需要選擇合適的文件夾進(jìn)行存放

      • hdpi/xhdpi/xxhdpi等等不同dpi的文件夾下的圖片在不同的設(shè)備上會經(jīng)過scale的處理。例如我們只在hdpi的目錄下放置了一張100100的圖片,那么根據(jù)換算關(guān)系,xxhdpi的手機去引用那張圖片就會被拉伸到200200。需要注意到在這種情況下,內(nèi)存占用是會顯著提高的。對于不希望被拉伸的圖片,需要放到assets或者nodpi的目錄下。

      10、謹(jǐn)慎使用static對象

      • static對象的生命周期過長,應(yīng)該謹(jǐn)慎使用

      11、謹(jǐn)慎使用單例中不合理的持有

      • 單例中的對象生命周期與應(yīng)用一致,注意不要在單例中進(jìn)行不必要的外界引用持有。如果一定要引用外部變量,需要在外部變量生命周期結(jié)束的時候接觸引用(賦為null)。

      12、一定要記得關(guān)閉無用連接

      • 在onDestory中關(guān)閉Cursor,I/O,數(shù)據(jù)庫,網(wǎng)絡(luò)的連接用完記得關(guān)閉。

      注意:謹(jǐn)慎使用lager heap

      • 不同的設(shè)備有不容的RAM,他們?yōu)閼?yīng)用程序設(shè)置了不同大小的Heap的閾值。雖然可以通過largeHeap=true的屬性來為應(yīng)用獲得一個更大的heap空間,然后通過getLargeMemoryClass()來獲取到這個更大的heap閾值。但是你要注意,largeHeap只是為了一些本來就需要大量內(nèi)存的APP存在,比如圖墻和圖片編輯軟件。所以,不要隨意的使用large heap,否則會影響系統(tǒng)整體的用戶體驗,會使每次gc時間更長。

      四、內(nèi)存泄露檢測

      • 這里介紹LeakCanary,一款非常好用的內(nèi)存泄露檢測工具,安裝在手機上,能夠通過Log的方式告訴你是哪塊代碼發(fā)生了內(nèi)存泄露。

      • 使用方法,在Application中install LeakCanary(默認(rèn)只能檢測Activity內(nèi)容的內(nèi)存泄露):

      public class MyApplication extends Application {
      
        @Override public void onCreate() {
          super.onCreate();
          LeakCanary.install(this);
        }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 想要檢測更多,首先注冊一個RefWatcher:
      public class MyApplication extends Application {
      
          private static RefWatcher sRefWatcher;
      
          @Override
          public void onCreate() {
              super.onCreate();
              sRefWatcher = LeakCanary.install(this);
          }
      
          public static RefWatcher getRefWatcher() {
              return sRefWatcher;
          }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 然后對某個可能發(fā)生泄露的占用大內(nèi)存的對象進(jìn)行監(jiān)測:
      MyApplication.getRefWatcher().watch(sLeaky);
      • 1
      • 1
      • 對Fragment、BroadcastReceiver、Service進(jìn)行監(jiān)測:
      public class MyFragment extends Fragment {
          @Override
          public void onDestroy() {
              super.onDestroy();
              MyApplication.getRefWatcher().watch(this);
          }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      參考文獻(xiàn)

        本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多