<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android性能之內(nèi)存泄漏

          共 5604字,需瀏覽 12分鐘

           ·

          2021-07-18 20:04

          和你一起終身學(xué)習(xí),這里是程序員Android

          經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點:

          一、什么是內(nèi)存泄漏?
          二、Android中導(dǎo)致內(nèi)存泄漏的主要幾個點
          三、Java虛擬機(jī)內(nèi)存管理
          四、Java內(nèi)存幾種分配策略?
          五、垃圾收集器是如何判斷對象是否可回收?
          六、什么是內(nèi)存抖動?
          七、內(nèi)存抖動產(chǎn)生的原因?
          八、Android中4種引用
          九、常見的導(dǎo)致內(nèi)存泄漏的示例

          下面我們就以上面幾個知識點來進(jìn)行逐一的分析:

          一、什么是內(nèi)存泄漏?

          當(dāng)一個對象已經(jīng)不需要在使用了,本應(yīng)該被回收,而另一個正在使用的對象持有它的引用,導(dǎo)致對象不能被回收。因為不能被及時回收的本該被回收的內(nèi)存,就產(chǎn)生了內(nèi)存泄漏。如果內(nèi)存泄漏太多會導(dǎo)致程序沒有辦法申請內(nèi)存,最后出現(xiàn)內(nèi)存溢出的錯誤。

          二、android中導(dǎo)致內(nèi)存泄漏的主要幾個點

          android開發(fā)中經(jīng)常出現(xiàn)的點,我有只有了解了,才能更好的避免。

          • 使用單例模式

          • 使用匿名內(nèi)部類

          • 使用異步事件處理機(jī)制Handler

          • 使用靜態(tài)變量

          • 資源未關(guān)閉

          • 設(shè)置監(jiān)聽

          • 使用AsyncTask

          • 使用Bitmap

          上面就是我列出的幾個常出現(xiàn)內(nèi)存泄漏的幾個點,下面我們將一一解讀。

          三、java虛擬機(jī)內(nèi)存管理

          java虛擬機(jī)內(nèi)存分為虛擬機(jī)棧,本地方法棧,程序計數(shù)器,堆,方法區(qū)這幾個模塊,下面我們就來分析下各個模塊。

          (1).虛擬機(jī)棧

          虛擬機(jī)棧主要的作用就是為執(zhí)行java方法服務(wù)的,是Java方法執(zhí)行的動態(tài)內(nèi)存模型。會導(dǎo)致棧內(nèi)存溢出(StackOverFlowError)

          (2).本地方法棧

          為執(zhí)行native方法服務(wù)的,其他和虛擬機(jī)棧一樣

          (3).程序計數(shù)器

          是當(dāng)前線程執(zhí)行的字節(jié)碼行號指示器
          處于線程獨占區(qū)
          如果是執(zhí)行的是java代碼,當(dāng)前值為字節(jié)碼指令的地址,如果是Native,值為undefined

          (4).堆

          存放對象的實例
          垃圾收集器管理的主要區(qū)域
          分代管理對象
          會導(dǎo)致內(nèi)存溢出(OutOfMemoryError)

          (5).方法區(qū)

          存放虛擬機(jī)加載的類信息,常量,靜態(tài)變量,編譯后的代碼和數(shù)據(jù)
          GC主要對方法區(qū)進(jìn)行常量回收和類卸載
          會出現(xiàn)內(nèi)存溢出(OutOfMemoryError)

          四、java內(nèi)存幾種分配策略?

          可以結(jié)合上面的內(nèi)存分配模型,能很好的理解。

          (1).靜態(tài)的

          靜態(tài)存儲區(qū):內(nèi)存在程序編譯期間就已經(jīng)分配完成,一般來說,這個區(qū)域在程序運(yùn)行期間一直處在
          它主要儲存靜態(tài)數(shù)據(jù),全局靜態(tài)數(shù)據(jù)和常量

          (2).棧式的

          執(zhí)行方法時,存儲局部變量(編譯期間,已經(jīng)確定占用內(nèi)存大小),操作數(shù),動態(tài)鏈接,方法出口

          (3).堆式的

          也叫動態(tài)內(nèi)存分配,主要存儲對象實例,以及已經(jīng)被加載類的Class對象(用于反射)

          五、垃圾收集器是如何判斷對象是否可回收?

          我們知道內(nèi)存泄漏的原因是應(yīng)該被回收的對象,不能被及時回收,那么GC是如何來判斷對象是否為垃圾對象呢?

          判斷的方式有兩個:

          • 引用計數(shù)
            對象被引用,引用計數(shù)器加1,反之減一,只有引用計數(shù)為0,那么這個對象為垃圾對象

          • 可達(dá)性
            從GCRoot節(jié)點對象開始,看是否可以訪問到此對象,如果沒有訪問到則為垃圾對象

          可以作為GCRoot對象有以下幾種:
          虛擬機(jī)棧中的局部變量
          本地方法棧中的引用對象
          方法區(qū)中的常量引用對象
          方法區(qū)中的類屬性引用對象
          在native層和早期的虛擬機(jī)一般使用引用計數(shù),但是現(xiàn)在的java虛擬機(jī)大多使用的是可達(dá)性。

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

          堆內(nèi)存都有一定的大小,能容納的數(shù)據(jù)是有限制的,當(dāng)Java堆的大小太大時,垃圾收集會啟動停止堆中不再應(yīng)用的對象,來釋放內(nèi)存。當(dāng)在極短時間內(nèi)分配給對象和回收對象的過程就是內(nèi)存抖動。

          七、內(nèi)存抖動產(chǎn)生的原因?

          從術(shù)語上來講就是極短時間內(nèi)分配給對象和回收對象的過程。
          一般多是在循環(huán)語句中創(chuàng)建臨時對象,在繪制時配置大量對象或者執(zhí)行動畫時創(chuàng)建大量臨時對象
          內(nèi)存抖動會帶來UI的卡頓,因為大量的對象創(chuàng)建,會很快消耗剩余內(nèi)存,導(dǎo)致GC回收,GC會占用大量的幀繪制時間,從而導(dǎo)致UI卡頓,關(guān)于UI卡頓會在后面章節(jié)講到。

          八、android中4種引用

          (1).StrongReference強(qiáng)引用
          從不被回收,java虛擬機(jī)停止時,才終止

          (2).SoftReference軟引用
          當(dāng)內(nèi)存不足時,會主動回收,使用SoftReference使用結(jié)合ReferenceQueue構(gòu)造有效期短

          (3).WeakReference弱引用
          每次垃圾回收時,被回收

          (4).PhatomReference虛引用
          每次垃圾回收時,被回收.結(jié)合ReferenceQueue來跟蹤對象被垃圾回收器回收的活動

          九、常見的導(dǎo)致內(nèi)存泄漏的示例

          (1).使用單例模式

              private static ComonUtil mInstance = null;
          private Context mContext = null;

          public ComonUtil(Context context) {
          mContext = context;
          }

          public static ComonUtil getInstance(Context context) {
          if (mInstance == null) {
          mInstance = new ComonUtil(context);
          }
          return mInstance;
          }

          使用:

          ComonUtil mComonUtil = ComonUtil.getInstance(this);
          我們看到上面的代碼就是我們平時使用的單例模式,當(dāng)然這里沒有考慮線程安全,請忽略。當(dāng)我們傳遞進(jìn)來的是Context,那么當(dāng)前對象就會持有第一次實例化的Context,如果Context是Activity對象,那么就會產(chǎn)生內(nèi)存泄漏。因為當(dāng)前對象ComonUtil是靜態(tài)的,生命周期和應(yīng)用是一樣的,只有應(yīng)用退出才會釋放,導(dǎo)致Activity不能及時釋放,帶來內(nèi)存泄漏。

          怎么解決呢?
          常見的有兩種方式,第一就是傳入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。

              public ComonUtil(Context context) {
          mContext = context.getApplicationContext();
          }

          (2).使用非靜態(tài)內(nèi)部類

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

          public void createNonStaticInnerClass(){
          CustomThread mCustomThread = new CustomThread();
          mCustomThread.start();
          }

          public class CustomThread extends Thread{
          @Override
          public void run() {
          super.run();
          while (true){
          try {
          Thread.sleep(5000);
          Log.i(TAG,"CustomThread ------- 打印");
          } catch (InterruptedException e) {
          e.printStackTrace();
          }
          }
          }
          }

          我們就以線程為例,當(dāng)Activity調(diào)用了createNonStaticInnerClass方法,然后退出當(dāng)前Activity時,因為線程還在后臺執(zhí)行且當(dāng)前線程持有Activity引用,只有等到線程執(zhí)行完畢,Activitiy才能得到釋放,導(dǎo)致內(nèi)存泄漏。
          常用的解決方法有很多,第一把線程類聲明為靜態(tài)的類,如果要用到Activity對象,那么就作為參數(shù)傳入且為WeakReference,第二在Activity的onDestroy時,停止線程的執(zhí)行。

          public static class CustomThread extends Thread{
          private WeakReference<MainActivity> mActivity;
          public CustomThread(MainActivity activity){
          mActivity = new WeakReference<MainActivity>(activity)
          }
          }

          (3).使用異步事件處理機(jī)制Handler

              /**
          * 異步消息處理機(jī)制 -- handler機(jī)制
          */

          public void createHandler(){
          mHandler.sendEmptyMessage(0);
          }
          public Handler mHandler = new Handler(new Handler.Callback() {
          @Override
          public boolean handleMessage(Message msg) {
          //處理耗時操作
          return false;
          }
          });

          這個應(yīng)該是我們平時使用最多的一種方式,如果當(dāng)handler中處理的是耗時操作,或者當(dāng)前消息隊列中消息很多時,那當(dāng)Activity退出時,當(dāng)前message中持有handler的引用,handler又持有Activity的引用,導(dǎo)致Activity不能及時的釋放,引起內(nèi)存泄漏的問題。
          解決handler引起的內(nèi)存泄漏問題常用的兩種方式:
          1.和上面解決Thread的方式一樣,
          2.在onDestroy中調(diào)用mHandler.removeCallbacksAndMessages(null)

              @Override
          protected void onDestroy() {
          super.onDestroy();
          mHandler.removeCallbacksAndMessages(null);
          }

          (4).使用靜態(tài)變量

          同單例引起的內(nèi)存泄漏。

          (5).資源未關(guān)閉

          常見的就是數(shù)據(jù)庫游標(biāo)沒有關(guān)閉,對象文件流沒有關(guān)閉,主要記得關(guān)閉就OK了。

          (6).設(shè)置監(jiān)聽

          常見的是在觀察者模式中出現(xiàn),我們在退出Acviity時沒有取消監(jiān)聽,導(dǎo)致被觀察者還持有當(dāng)前Activity的引用,從而引起內(nèi)存泄漏。
          常見的解決方法就是在onPause中注消監(jiān)聽

          (7).使用AsyncTask

              public AsyncTask<Object, Object, Object> mTask = new AsyncTask<Object, Object, Object>() {

          @Override
          protected Object doInBackground(Object... params) {
          //耗時操作
          return null;
          }

          @Override
          protected void onPostExecute(Object o) {

          }
          };

          和上面同樣的道理,匿名內(nèi)部類持有外部類的引用,AsyncTask耗時操作導(dǎo)致Activity不能及時釋放,引起內(nèi)存泄漏。
          解決方法同上:
          1.聲明為靜態(tài)類,
          2.在onPause中取消任務(wù)

          (8).使用Bitmap

          我們知道當(dāng)bitmap對象沒有被使用(引用),gc會回收bitmap的占用內(nèi)存,當(dāng)時這邊的內(nèi)存指的是java層的,那么本地內(nèi)存的釋放呢?我們可以通過調(diào)用bitmap.recycle()來釋放C層上的內(nèi)存,防止本地內(nèi)存泄漏

          原文鏈接:https://www.jianshu.com/p/797395731747

          友情推薦:

          Android 開發(fā)干貨集錦

          至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時期待您的關(guān)注,感謝您的閱讀,謝謝!

          點個在看,方便您使用時快速查找!

          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  三级片无码一区 | 成人三级电影久久 | 五月丁香花网 | 天天澡天天日天天射天天舔天天爽爽爽 | 人人妻人人操人人摸 |