Java內(nèi)存分配策略
java程序運(yùn)行時(shí)的內(nèi)存分配策略有三種,分別是靜態(tài)分配鬼店,棧式分配,堆式分配黔龟。對應(yīng)的三種存儲策略使用的內(nèi)存空間主要分別是靜態(tài)存儲區(qū)(方法區(qū))妇智,棧區(qū),堆區(qū)氏身。
靜態(tài)存儲區(qū):主要存放靜態(tài)數(shù)據(jù)巍棱,全局static數(shù)據(jù)和常量。這塊內(nèi)存在程序編譯時(shí)就已經(jīng)分配好蛋欣,并且在程序整個(gè)運(yùn)行期間都存在航徙。
棧區(qū):當(dāng)方法被執(zhí)行時(shí),方法體內(nèi)的局部變量都在棧上創(chuàng)建陷虎,并在方法執(zhí)行結(jié)束時(shí)這些局部變量所持有的內(nèi)存將會被自動釋放到踏。
堆區(qū):程序運(yùn)行時(shí)直接new出來的內(nèi)存,這部分內(nèi)存在不使用時(shí)將會由gc來負(fù)責(zé)回收泻红。
Java四種引用
強(qiáng)引用:JVM 寧可拋出 OOM 夭禽,也不會讓 GC 回收具有強(qiáng)引用的對象;
軟引用:只有在內(nèi)存空間不足時(shí)谊路,才會被回的對象讹躯;
弱引用:在 GC 時(shí),一旦發(fā)現(xiàn)了只具有弱引用的對象缠劝,不管當(dāng)前內(nèi)存空間足夠與否潮梯,都會回收它的內(nèi)存;
虛引用:任何時(shí)候都可以被GC回收
我們常說的內(nèi)存泄漏是指new出來的Object無法被GC回收惨恭,即為強(qiáng)引用
Android常見的內(nèi)存泄漏案例
一秉馏、單例造成的內(nèi)存泄漏
由于單例靜態(tài)特性使得單例的生命周期和應(yīng)用的生命周期一樣長。如果一個(gè)對象已經(jīng)不需要使用了脱羡,而單例對象還持有該對象的引用萝究,那么這個(gè)對象將不能被正常回收锉罐,導(dǎo)致了內(nèi)存泄漏帆竹。如下這個(gè)典例:
這是一個(gè)普通的單例模式,當(dāng)創(chuàng)建這個(gè)單例的時(shí)候脓规,由于需要傳入一個(gè)Context栽连,所以這個(gè)Context的生命周期的長短至關(guān)重要:
1、傳入的是Application的Context:這將沒有任何問題,因?yàn)閱卫纳芷诤虯pplication的一樣長
2秒紧、傳入的是Activity的Context:當(dāng)這個(gè)Context所對應(yīng)的Activity退出時(shí)绢陌,由于單例對象還持有該Activity的引用,該Activity不能被正橙刍郑回收脐湾。
所以正確的單例應(yīng)該修改為下面這種方式:
這樣不管傳入什么Context最終將使用Application的Context,而單例的生命周期和應(yīng)用的一樣長绩聘,這樣就防止了內(nèi)存泄漏沥割。
二、非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例造成的內(nèi)存泄漏
有時(shí)候我們可能會出現(xiàn)這樣的寫法
這樣就在Activity內(nèi)部創(chuàng)建了一個(gè)非靜態(tài)內(nèi)部類的單例凿菩,每次啟動Activity時(shí)都會使用該單例的數(shù)據(jù),這樣雖然避免了資源的重復(fù)創(chuàng)建帜讲,不過這種寫法卻會造成內(nèi)存泄漏衅谷,因?yàn)榉庆o態(tài)內(nèi)部類默認(rèn)會持有外部類的引用,而又使用了該非靜態(tài)內(nèi)部類創(chuàng)建了一個(gè)靜態(tài)的實(shí)例似将,該實(shí)例的生命周期和應(yīng)用的一樣長获黔,這就導(dǎo)致了該靜態(tài)實(shí)例一直會持有該Activity的引用,導(dǎo)致Activity的內(nèi)存資源不能正吃谘椋回收玷氏。
正確的做法為:將該內(nèi)部類設(shè)為靜態(tài)內(nèi)部類或?qū)⒃搩?nèi)部類抽取出來封裝成一個(gè)單例,如果需要使用Context腋舌,使用ApplicationContext
三盏触、Handler造成的內(nèi)存泄漏
Handler的使用造成的內(nèi)存泄漏問題應(yīng)該說最為常見了,平時(shí)在處理網(wǎng)絡(luò)任務(wù)或者封裝一些請求回調(diào)等api都應(yīng)該會借助Handler來處理块饺,對于Handler的使用代碼編寫一不規(guī)范即有可能造成內(nèi)存泄漏赞辩,如下示例:
這種創(chuàng)建Handler的方式會造成內(nèi)存泄漏,由于mHandler是Handler的非靜態(tài)匿名內(nèi)部類的實(shí)例授艰,所以它持有外部類Activity的引用辨嗽,而消息隊(duì)列是在一個(gè)Looper線程中不斷輪詢處理消息,那么當(dāng)這個(gè)Activity退出時(shí)消息隊(duì)列中還有未處理的消息或者正在處理消息淮腾,而消息隊(duì)列中的Message持有mHandler實(shí)例的引用糟需,mHandler又持有Activity的引用,所以導(dǎo)致該Activity的內(nèi)存資源無法及時(shí)回收谷朝,引發(fā)內(nèi)存泄漏洲押,所以另外一種做法為:
創(chuàng)建一個(gè)靜態(tài)Handler內(nèi)部類,然后對Handler持有的對象使用弱引用徘禁,這樣在回收時(shí)就可以回收Handler持有的對象诅诱,這樣雖然避免了Activity泄漏,不過Looper線程的消息隊(duì)列中還是可能會有待處理的消息送朱,所以我們在Activity的Destroy時(shí)或者Stop時(shí)應(yīng)該移除消息隊(duì)列中的消息
使用mHandler.removeCallbacksAndMessages(null);是移除消息隊(duì)列中所有消息和所有的Runnable娘荡。
四干旁、資源未關(guān)閉造成的內(nèi)存泄漏
對于使用了BraodcastReceiver,ContentObserver炮沐,F(xiàn)ile争群,Cursor,Stream大年,Bitmap等資源的使用换薄,應(yīng)該在Activity銷毀時(shí)及時(shí)關(guān)閉或者注銷,否則這些資源將不會被回收翔试,造成內(nèi)存泄漏轻要。
10 條提升 Android 性能的建議
https://academy.realm.io/cn/posts/droidcon-farber-improving-android-app-performance/