欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的內(nèi)存泄露問題和解決辦法

 更新時間:2022年01月26日 15:15:29   作者:duoduo18up  
大家好,本篇文章主要講的是Java中的內(nèi)存泄露問題和解決辦法,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下

(Memory Leak,內(nèi)存泄漏)

為什么會產(chǎn)生內(nèi)存泄漏?

當一個對象已經(jīng)不需要再使用本該被回收時,另外一個正在使用的對象持有它的引用從而導致它不能被回收,這導致本該被回收的對象不能被回收而停留在堆內(nèi)存中,這就產(chǎn)生了內(nèi)存泄漏。

內(nèi)存泄漏對程序的影響?

內(nèi)存泄漏是造成應用程序OOM的主要原因之一。我們知道Android系統(tǒng)為每個應用程序分配的內(nèi)存是有限的,而當一個應用中產(chǎn)生的內(nèi)存泄漏比較多時,這就難免會導致應用所需要的內(nèi)存超過系統(tǒng)分配的內(nèi)存限額,這就造成了內(nèi)存溢出從而導致應用Crash。

如何檢查和分析內(nèi)存泄漏?

因為內(nèi)存泄漏是在堆內(nèi)存中,所以對我們來說并不是可見的。通常我們可以借助MAT、LeakCanary等工具來檢測應用程序是否存在內(nèi)存泄漏。
1、MAT是一款強大的內(nèi)存分析工具,功能繁多而復雜。
2、LeakCanary則是由Square開源的一款輕量級的第三方內(nèi)存泄漏檢測工具,當檢測到程序中產(chǎn)生內(nèi)存泄漏時,它將以最直觀的方式告訴我們哪里產(chǎn)生了內(nèi)存泄漏和導致誰泄漏了而不能被回收。

常見的內(nèi)存泄漏及解決方法

1、單例造成的內(nèi)存泄漏

由于單例的靜態(tài)特性使得其生命周期和應用的生命周期一樣長,如果一個對象已經(jīng)不再需要使用了,而單例對象還持有該對象的引用,就會使得該對象不能被正?;厥?,從而導致了內(nèi)存泄漏。
示例:防止單例導致內(nèi)存泄漏的實例

// 使用了單例模式
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

這樣不管傳入什么Context最終將使用Application的Context,而單例的生命周期和應用的一樣長,這樣就防止了內(nèi)存泄漏。???

2、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例造成的內(nèi)存泄漏【已無】

例如,有時候我們可能會在啟動頻繁的Activity中,為了避免重復創(chuàng)建相同的數(shù)據(jù)資源,可能會出現(xiàn)如下寫法:

public class MainActivity extends AppCompatActivity {
 
    private static TestResource mResource = null;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}

這樣在Activity內(nèi)部創(chuàng)建了一個非靜態(tài)內(nèi)部類的單例,每次啟動Activity時都會使用該單例的數(shù)據(jù)。雖然這樣避免了資源的重復創(chuàng)建,但是這種寫法卻會造成內(nèi)存泄漏。因為非靜態(tài)內(nèi)部類默認會持有外部類的引用,而該非靜態(tài)內(nèi)部類又創(chuàng)建了一個靜態(tài)的實例,該實例的生命周期和應用的一樣長,這就導致了該靜態(tài)實例一直會持有該Activity的引用,從而導致Activity的內(nèi)存資源不能被正常回收。
解決方法:將該內(nèi)部類設為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來封裝成一個單例,如果需要使用Context,就使用Application的Context。

3、Handler造成的內(nèi)存泄漏

示例:創(chuàng)建匿名內(nèi)部類的靜態(tài)對象

public class MainActivity extends AppCompatActivity {
 
    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}

1、從Android的角度
當Android應用程序啟動時,該應用程序的主線程會自動創(chuàng)建一個Looper對象和與之關聯(lián)的MessageQueue。當主線程中實例化一個Handler對象后,它就會自動與主線程Looper的MessageQueue關聯(lián)起來。所有發(fā)送到MessageQueue的Messag都會持有Handler的引用,所以Looper會據(jù)此回調(diào)Handle的handleMessage()方法來處理消息。只要MessageQueue中有未處理的Message,Looper就會不斷的從中取出并交給Handler處理。另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。
2、 Java角度
在Java中,非靜態(tài)內(nèi)部類和匿名類內(nèi)部類都會潛在持有它們所屬的外部類的引用,但是靜態(tài)內(nèi)部類卻不會。

對上述的示例進行分析,當MainActivity結束時,未處理的消息持有handler的引用,而handler又持有它所屬的外部類也就是MainActivity的引用。這條引用關系會一直保持直到消息得到處理,這樣阻止了MainActivity被垃圾回收器回收,從而造成了內(nèi)存泄漏。
解決方法:將Handler類獨立出來或者使用靜態(tài)內(nèi)部類,這樣便可以避免內(nèi)存泄漏。

4、線程造成的內(nèi)存泄漏

示例:AsyncTask和Runnable

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    }
 
    class MyAsyncTask extends AsyncTask<Void, Void, Void> {
 
        // ...
 
        public MyAsyncTask(Context context) {
            // ...
        }
 
        @Override
        protected Void doInBackground(Void... params) {
            // ...
            return null;
        }
 
        @Override
        protected void onPostExecute(Void aVoid) {
            // ...
        }
    }
 
    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // ...
        }
    }
}

AsyncTask和Runnable都使用了匿名內(nèi)部類,那么它們將持有其所在Activity的隱式引用。如果任務在Activity銷毀之前還未完成,那么將導致Activity的內(nèi)存資源無法被回收,從而造成內(nèi)存泄漏。
解決方法:將AsyncTask和Runnable類獨立出來或者使用靜態(tài)內(nèi)部類,這樣便可以避免內(nèi)存泄漏。

5、資源未關閉造成的內(nèi)存泄漏

對于使用了BraodcastReceiver,ContentObserver,F(xiàn)ile,Cursor,Stream,Bitmap等資源,應該在Activity銷毀時及時關閉或者注銷,否則這些資源將不會被回收,從而造成內(nèi)存泄漏。

1)比如在Activity中register了一個BraodcastReceiver,但在Activity結束后沒有unregister該BraodcastReceiver。
2)資源性對象比如Cursor,Stream、File文件等往往都用了一些緩沖,我們在不使用的時候,應該及時關閉它們,以便它們的緩沖及時回收內(nèi)存。它們的緩沖不僅存在于 java虛擬機內(nèi),還存在于java虛擬機外。如果我們僅僅是把它的引用設置為null,而不關閉它們,往往會造成內(nèi)存泄漏。
3)對于資源性對象在不使用的時候,應該調(diào)用它的close()函數(shù)將其關閉掉,然后再設置為null。在我們的程序退出時一定要確保我們的資源性對象已經(jīng)關閉。
4)Bitmap對象不在使用時調(diào)用recycle()釋放內(nèi)存。2.3以后的bitmap應該是不需要手動recycle了,內(nèi)存已經(jīng)在java層了。

6、使用ListView時造成的內(nèi)存泄漏

初始時ListView會從BaseAdapter中根據(jù)當前的屏幕布局實例化一定數(shù)量的View對象,同時ListView會將這些View對象緩存起來。當向上滾動ListView時,原先位于最上面的Item的View對象會被回收,然后被用來構造新出現(xiàn)在下面的Item。這個構造過程就是由getView()方法完成的,getView()的第二個形參convertView就是被緩存起來的Item的View對象(初始化時緩存中沒有View對象則convertView是null)。

構造Adapter時,沒有使用緩存的convertView。
解決方法:在構造Adapter時,使用緩存的convertView。

7、集合容器中的內(nèi)存泄露

我們通常把一些對象的引用加入到了集合容器(比如ArrayList)中,當我們不需要該對象時,并沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。
解決方法:在退出程序之前,將集合里的東西clear,然后置為null,再退出程序。

8、WebView造成的泄露

當我們不要使用WebView對象時,應該調(diào)用它的destory()函數(shù)來銷毀它,并釋放其占用的內(nèi)存,否則其長期占用的內(nèi)存也不能被回收,從而造成內(nèi)存泄露。
解決方法:為WebView另外開啟一個進程,通過AIDL與主線程進行通信,WebView所在的進程可以根據(jù)業(yè)務的需要選擇合適的時機進行銷毀,從而達到內(nèi)存的完整釋放。

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

1、在涉及使用Context時,對于生命周期比Activity長的對象應該使用Application的Context。凡是使用Context優(yōu)先考慮Application的Context,當然它并不是萬能的,對于有些地方則必須使用Activity的Context。對于Application,Service,Activity三者的Context的應用場景如下:

其中,NO1表示Application和Service可以啟動一個Activity,不過需要創(chuàng)建一個新的task任務隊列。而對于Dialog而言,只有在Activity中才能創(chuàng)建。除此之外三者都可以使用。

2、對于需要在靜態(tài)內(nèi)部類中使用非靜態(tài)外部成員變量(如:Context、View ),可以在靜態(tài)內(nèi)部類中使用弱引用來引用外部類的變量來避免內(nèi)存泄漏。
3、對于不再需要使用的對象,顯示的將其賦值為null,比如使用完Bitmap后先調(diào)用recycle(),再賦為null。
4、保持對對象生命周期的敏感,特別注意單例、靜態(tài)對象、全局性集合等的生命周期。
5、對于生命周期比Activity長的內(nèi)部類對象,并且內(nèi)部類中使用了外部類的成員變量,可以這樣做避免內(nèi)存泄漏:
1)將內(nèi)部類改為靜態(tài)內(nèi)部類
2)靜態(tài)內(nèi)部類中使用弱引用來引用外部類的成員變量

總結

到此這篇關于Java中的內(nèi)存泄露問題和解決辦法的文章就介紹到這了,更多相關Java內(nèi)存泄露問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 超詳細的Intellij IDEA 看源碼必備技能

    超詳細的Intellij IDEA 看源碼必備技能

    這篇文章主要介紹了Intellij IDEA 看源碼必備技能,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Java讀寫txt文件代碼實例

    Java讀寫txt文件代碼實例

    這篇文章主要給大家介紹了關于Java讀寫txt文件的相關資料,近期處理的數(shù)據(jù)規(guī)模比較大,正好又是統(tǒng)計合并的事情,想著借助excel就可以完成了,然后就了解了下java讀取excel的事情,需要的朋友可以參考下
    2023-09-09
  • IntelliJ IDEA 2019.1.1 for MAC 下載和注冊碼激活教程圖解

    IntelliJ IDEA 2019.1.1 for MAC 下載和注

    這篇文章主要介紹了IntelliJ IDEA 2019.1.1 for MAC 下載和注冊碼激活,教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • Spring事務處理Transactional,鎖同步和并發(fā)線程

    Spring事務處理Transactional,鎖同步和并發(fā)線程

    本文詳細講解了Spring事務處理Transactional,鎖同步和并發(fā)線程。對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-12-12
  • 在Java中使用Jwt的示例代碼

    在Java中使用Jwt的示例代碼

    這篇文章主要介紹了在Java中使用Jwt的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • javac、java打jar包命令實例

    javac、java打jar包命令實例

    這篇文章主要演示Java中使用命令打jar包的實例過程,很實用,希望能給大家做一個參考。
    2016-06-06
  • Java中將List拆分為多個小list集合的實現(xiàn)代碼

    Java中將List拆分為多個小list集合的實現(xiàn)代碼

    這篇文章主要介紹了Java中如何將List拆分為多個小list集合,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • java版十大排序經(jīng)典算法:完整代碼(3)

    java版十大排序經(jīng)典算法:完整代碼(3)

    優(yōu)秀的文章也不少,但是Java完整版的好像不多,我把所有的寫一遍鞏固下,同時也真誠的希望閱讀到這篇文章的小伙伴們可以自己去從頭敲一遍,不要粘貼復制!希望我的文章對你有所幫助,每天進步一點點
    2021-07-07
  • Java中的ConcurrentLinkedQueue使用解析

    Java中的ConcurrentLinkedQueue使用解析

    這篇文章主要介紹了Java中的ConcurrentLinkedQueue使用解析,一個基于鏈接節(jié)點的無界線程安全隊列,此隊列按照 FIFO(先進先出)原則對元素進行排序,隊列的頭部是隊列中時間最長的元素,需要的朋友可以參考下
    2023-12-12
  • springboot2.2 集成 activity6實現(xiàn)請假流程(示例詳解)

    springboot2.2 集成 activity6實現(xiàn)請假流程(示例詳解)

    這篇文章主要介紹了springboot2.2 集成 activity6實現(xiàn)請假完整流程示例詳解,本文通過示例代碼圖文相結合給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07

最新評論