0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學(xué)習在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區
會(huì )員中心
創(chuàng )作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內不再提示

什么是內存泄漏?android中導致內存泄漏的主要幾個(gè)點(diǎn)

哆啦安全 ? 來(lái)源:程序員Android ? 2024-02-20 10:17 ? 次閱讀

一、什么是內存泄漏?

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

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

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

使用單例模式

使用匿名內部類(lèi)

使用異步事件處理機制Handler

使用靜態(tài)變量

資源未關(guān)閉

設置監聽(tīng)

使用AsyncTask

使用Bitmap

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

三、java虛擬機內存管理

5f5b9194-cf87-11ee-a297-92fbcf53809c.jpg

java虛擬機內存分為虛擬機棧,本地方法棧,程序計數器,堆,方法區這幾個(gè)模塊,下面我們就來(lái)分析下各個(gè)模塊。

(1).虛擬機棧

虛擬機棧主要的作用就是為執行java方法服務(wù)的,是Java方法執行的動(dòng)態(tài)內存模型。會(huì )導致棧內存溢出(StackOverFlowError)

(2).本地方法棧

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

(3).程序計數器

是當前線(xiàn)程執行的字節碼行號指示器
處于線(xiàn)程獨占區
如果是執行的是java代碼,當前值為字節碼指令的地址,如果是Native,值為undefined

(4).堆

存放對象的實(shí)例
垃圾收集器管理的主要區域
分代管理對象
會(huì )導致內存溢出(OutOfMemoryError)

(5).方法區

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

四、java內存幾種分配策略?

可以結合上面的內存分配模型,能很好的理解。

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

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

(2).棧式的

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

(3).堆式的

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

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

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

判斷的方式有兩個(gè):

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

可達性
從GCRoot節點(diǎn)對象開(kāi)始,看是否可以訪(fǎng)問(wèn)到此對象,如果沒(méi)有訪(fǎng)問(wèn)到則為垃圾對象

可以作為GCRoot對象有以下幾種:
虛擬機棧中的局部變量
本地方法棧中的引用對象
方法區中的常量引用對象
方法區中的類(lèi)屬性引用對象
在native層和早期的虛擬機一般使用引用計數,但是現在的java虛擬機大多使用的是可達性。

六、什么是內存抖動(dòng)?

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

七、內存抖動(dòng)產(chǎn)生的原因?

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

八、android中4種引用

(1).StrongReference強引用
從不被回收,java虛擬機停止時(shí),才終止

(2).SoftReference軟引用
當內存不足時(shí),會(huì )主動(dòng)回收,使用SoftReference使用結合ReferenceQueue構造有效期短

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

(4).PhatomReference虛引用
每次垃圾回收時(shí),被回收.結合ReferenceQueue來(lái)跟蹤對象被垃圾回收器回收的活動(dòng)

九、常見(jiàn)的導致內存泄漏的示例

(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);

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

怎么解決呢?

常見(jiàn)的有兩種方式,第一就是傳入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。

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

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

    /**
     * 非靜態(tài)內部類(lè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();
                }
            }
        }
    }

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

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

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

    /**
     * 異步消息處理機制  -- handler機制
     */
    public void createHandler(){
        mHandler.sendEmptyMessage(0);
    }
    public Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //處理耗時(shí)操作   
            return false;
        }
    });

這個(gè)應該是我們平時(shí)使用最多的一種方式,如果當handler中處理的是耗時(shí)操作,或者當前消息隊列中消息很多時(shí),那當Activity退出時(shí),當前message中持有handler的引用,handler又持有Activity的引用,導致Activity不能及時(shí)的釋放,引起內存泄漏的問(wèn)題。

解決handler引起的內存泄漏問(wèn)題常用的兩種方式:

1.和上面解決Thread的方式一樣,

2.在onDestroy中調用mHandler.removeCallbacksAndMessages(null)

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

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

同單例引起的內存泄漏。

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

常見(jiàn)的就是數據庫游標沒(méi)有關(guān)閉,對象文件流沒(méi)有關(guān)閉,主要記得關(guān)閉就OK了。

(6).設置監聽(tīng)

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

(7).使用AsyncTask

    public AsyncTask mTask = new AsyncTask() {

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

        @Override
        protected void onPostExecute(Object o) {
        
        }   
    };

和上面同樣的道理,匿名內部類(lèi)持有外部類(lèi)的引用,AsyncTask耗時(shí)操作導致Activity不能及時(shí)釋放,引起內存泄漏。

解決方法同上:

1.聲明為靜態(tài)類(lèi),
2.在onPause中取消任務(wù)

(8).使用Bitmap

我們知道當bitmap對象沒(méi)有被使用(引用),gc會(huì )回收bitmap的占用內存,當時(shí)這邊的內存指的是java層的,那么本地內存的釋放呢?我們可以通過(guò)調用bitmap.recycle()來(lái)釋放C層上的內存,防止本地內存泄漏




審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權轉載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習之用,如有內容侵權或者其他違規問(wèn)題,請聯(lián)系本站處理。 舉報投訴
  • Android
    +關(guān)注

    關(guān)注

    12

    文章

    3882

    瀏覽量

    126019
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2912

    瀏覽量

    103283
  • 虛擬機
    +關(guān)注

    關(guān)注

    1

    文章

    868

    瀏覽量

    27558
  • 內存泄漏
    +關(guān)注

    關(guān)注

    0

    文章

    38

    瀏覽量

    9176

原文標題:Android內存泄漏知識點(diǎn)

文章出處:【微信號:哆啦安全,微信公眾號:哆啦安全】歡迎添加關(guān)注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Linux內存泄漏檢測實(shí)現原理與實(shí)現

    在使用沒(méi)有垃圾回收的語(yǔ)言時(shí)(如 C/C++),可能由于忘記釋放內存導致內存被耗盡,這叫 內存泄漏。由于內核也需要自己管理
    發(fā)表于 12-09 11:11 ?880次閱讀

    內存泄漏定位該如何去實(shí)現呢

    嵌入式之內存泄漏定位篇在嵌入式開(kāi)發(fā)中,經(jīng)常會(huì )使用malloc,free分配釋放堆內存,當malloc,free不配對使用時(shí),就會(huì )導致內存
    發(fā)表于 12-17 07:24

    分享一種內存泄漏定位排查技巧

    常見(jiàn)的泄漏方式在嵌入式開(kāi)發(fā)中,經(jīng)常會(huì )使用malloc,free分配釋放堆內存,稍不小心就可能導致內存點(diǎn)點(diǎn)地泄露,直至堆
    發(fā)表于 12-17 08:13

    內存泄漏的特點(diǎn)和類(lèi)型

    在計算機科學(xué)中,內存泄漏(memory leak)指由于疏忽或錯誤使程序未能釋放而造成不能再使用的內存的情況。內存泄漏并非指
    的頭像 發(fā)表于 06-20 10:58 ?2607次閱讀

    內存泄漏問(wèn)題原理及檢視方法

    可能不少開(kāi)發(fā)者都遇到過(guò)內存泄漏導致的網(wǎng)上問(wèn)題,具體表現為單板在現網(wǎng)運行數月以后,因為內存耗盡而導致單板復位現象。一方面,
    的頭像 發(fā)表于 10-10 10:42 ?2323次閱讀

    什么是內存泄漏?內存泄漏有哪些現象

    內存泄漏幾乎是很難避免的,不管是老手還是新手,都存在這個(gè)問(wèn)題,甚至 Windows 與 Linux 這類(lèi)系統軟件也或多或少存在著(zhù)內存泄漏。
    的頭像 發(fā)表于 09-05 17:24 ?9128次閱讀

    怎么解決C語(yǔ)言中的內存泄漏問(wèn)題呢?

    只有在堆內存里面才會(huì )發(fā)生內存泄漏的問(wèn)題,在棧內存中不會(huì )發(fā)生內存泄漏。因為棧
    發(fā)表于 06-11 17:31 ?434次閱讀
    怎么解決C語(yǔ)言中的<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>問(wèn)題呢?

    Linux內存泄漏檢測實(shí)現原理與實(shí)現

    在使用沒(méi)有垃圾回收的語(yǔ)言時(shí)(如 C/C++),可能由于忘記釋放內存導致內存被耗盡,這叫 內存泄漏。
    的頭像 發(fā)表于 07-03 09:21 ?459次閱讀
    Linux<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>檢測實(shí)現原理與實(shí)現

    什么是內存泄漏?如何避免JavaScript內存泄漏

    JavaScript 代碼中常見(jiàn)的內存泄漏的常見(jiàn)來(lái)源: 研究內存泄漏問(wèn)題就相當于尋找符合垃圾回收機制的編程方式,有效避免對象引用的問(wèn)題。
    發(fā)表于 10-27 11:30 ?168次閱讀
    什么是<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>?如何避免JavaScript<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>

    內存泄漏如何避免

    的數,那就是內存溢出。 2. 內存泄漏 內存泄露 memory leak,是指程序在申請內存后,無(wú)法釋放已申請的
    的頭像 發(fā)表于 11-10 11:04 ?433次閱讀
    <b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>如何避免

    內存泄漏會(huì )產(chǎn)生哪些后果

    內存泄漏原因 內存泄漏在C/C++這種不帶GC(Garbage Collection)的語(yǔ)言里,是一個(gè)經(jīng)常發(fā)生的問(wèn)題。因為沒(méi)有GC,所以分配的內存
    的頭像 發(fā)表于 11-10 15:06 ?497次閱讀
    <b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>會(huì )產(chǎn)生哪些后果

    線(xiàn)程內存泄漏問(wèn)題的定位

    記錄一個(gè)關(guān)于線(xiàn)程內存泄漏問(wèn)題的定位過(guò)程,以及過(guò)程中的收獲。 1. 初步定位 是否存在內存泄漏:想到內存
    的頭像 發(fā)表于 11-13 11:38 ?392次閱讀
    線(xiàn)程<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>問(wèn)題的定位

    如何發(fā)現內存泄漏

    由于 C 和 C++ 程序中完全由程序員自主申請和釋放內存,稍不注意,就會(huì )在系統中導入內存錯誤。同時(shí),內存錯誤往往非常嚴重,一般會(huì )帶來(lái)諸如系統崩潰,內存耗盡這樣嚴重的 后果。本文將從靜
    的頭像 發(fā)表于 11-13 15:41 ?353次閱讀

    內存溢出與內存泄漏:定義、區別與解決方案

    與區別 1. 定義: 內存溢出(Memory Overflow)指的是程序在申請內存時(shí),無(wú)法獲得足夠的內存空間,導致程序拋出異?;虮罎?。當程序需要的
    的頭像 發(fā)表于 12-19 14:10 ?1422次閱讀

    C語(yǔ)言內存泄漏問(wèn)題原理

    內存泄漏問(wèn)題只有在使用堆內存的時(shí)候才會(huì )出現,棧內存不存在內存泄漏問(wèn)題,因為棧
    發(fā)表于 03-19 11:38 ?261次閱讀
    C語(yǔ)言<b class='flag-5'>內存</b><b class='flag-5'>泄漏</b>問(wèn)題原理
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看