內(nèi)存泄露

1、java內(nèi)存泄漏基礎(chǔ)知識(shí)

簡(jiǎn)單的講就是該被釋放的對(duì)象沒(méi)有得到釋放,一直被某個(gè)實(shí)例所持有,導(dǎo)致不能垃圾回收。

2.java內(nèi)存的分配策略

1)java程序運(yùn)行當(dāng)中,它的內(nèi)存分配策略是分為三個(gè)部分。

(一)靜態(tài)存儲(chǔ)區(qū)(也叫方法區(qū)):此處主要存放一些靜態(tài)數(shù)據(jù)、全局變量等等。在java內(nèi)存當(dāng)中,程序編譯的時(shí)候,他已經(jīng)分配好了內(nèi)存,并且在靜態(tài)存儲(chǔ)區(qū)中存儲(chǔ)的變量,在整個(gè)程序運(yùn)行期間都存在。

?(二)棧區(qū):他被方法執(zhí)行的時(shí)候,方法體內(nèi)的局部變量,它會(huì)在棧內(nèi)創(chuàng)建內(nèi)存空間,并在方法執(zhí)行結(jié)束后,這些變量所持有的內(nèi)存,他會(huì)被自動(dòng)釋放。因?yàn)檎純?nèi)存分配運(yùn)算,它內(nèi)置于處理器中,所以它效率很高。但是棧區(qū) 的內(nèi)存空間容量有限。(由操作系統(tǒng)自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等

?(三)堆區(qū)(動(dòng)態(tài)內(nèi)存分配):通常就是我們new對(duì)象出來(lái)的內(nèi)存,這部分內(nèi)存在不使用的時(shí)候,他將會(huì)有java 的垃圾回收器來(lái)負(fù)責(zé)回收。

棧區(qū)和堆區(qū)的區(qū)別:棧區(qū)在方法體內(nèi)定義的一些基本類型變量? 和對(duì)象的引用變量,都是在方法的棧內(nèi)存中分配的。當(dāng)在一段方法中定義一個(gè)

變量時(shí),java就會(huì)在棧中為該變量分配內(nèi)存空間,當(dāng)超過(guò)變量的作用域之后,這個(gè)變量也就無(wú)效了。分配給他的內(nèi)存空間也將被釋放。

堆區(qū)所存儲(chǔ)的是所有new出來(lái)的對(duì)象還有數(shù)組,在堆中分配的內(nèi)存由java垃圾回收器自動(dòng)管理。產(chǎn)生的數(shù)組或者對(duì)象可以在棧中定義一個(gè)特殊變量(棧區(qū)存儲(chǔ)內(nèi)存的地址)

3.java是如何管理內(nèi)存的。

答:簡(jiǎn)單來(lái)說(shuō),java管理內(nèi)存就是對(duì)象的分配和釋放問(wèn)題,在java中啊,程序員需要new為每個(gè)對(duì)象分配內(nèi)存空間,所有的對(duì)象都是在堆中分配空間的。對(duì)象的釋放是有GC(垃圾回收器)決定,在java中內(nèi)存分配是由我們開(kāi)發(fā)者來(lái)完成的,而內(nèi)存的釋放是由GC來(lái)自動(dòng)完成的。這兩條先減輕了我們開(kāi)發(fā)者的工作,但是加重了java虛擬機(jī)的工作,這也是java相比較C語(yǔ)言、C++運(yùn)行較慢的原因之一。但是GC有很大的作用,它為了能夠正確的釋放對(duì)象,他必須監(jiān)控每個(gè)對(duì)象的運(yùn)行狀態(tài),包括對(duì)象的申請(qǐng)、引用、被引用、賦值等等都需要進(jìn)行監(jiān)控。

4.java中的內(nèi)存泄露?

答:內(nèi)存泄露是指無(wú)用對(duì)象(不再被使用的對(duì)象)持續(xù)占有內(nèi)存或者無(wú)用對(duì)象的內(nèi)存得不到釋放,從而造成的內(nèi)存空間的浪費(fèi),稱之為內(nèi)存泄露。內(nèi)存泄露不斷地積累就會(huì)造成OOM現(xiàn)象

5.java中的內(nèi)存泄漏由哪些原因?qū)е拢?/b>

單例、匿名內(nèi)部類、handler造成的內(nèi)存泄露等、static亂用、資源未關(guān)閉、AsyncTask等

(1)單例的靜態(tài)特性,使他生命周期和APP的生命周期一樣長(zhǎng),這也就表明如果一個(gè)對(duì)象不再被使用了,而這個(gè)單例還持有該對(duì)象的引用,被釋放的對(duì)象將不能被正?;厥?。就會(huì)造成內(nèi)存泄露。

(2)匿名內(nèi)部類

非靜態(tài)內(nèi)部類默認(rèn)會(huì)持有外部類的MainActivity的引用,而用非靜態(tài)內(nèi)部類創(chuàng)建一個(gè)靜態(tài)實(shí)例的話,該實(shí)例的聲明周期就會(huì)和應(yīng)用的生命周期一樣長(zhǎng)。這就導(dǎo)致了該靜態(tài)實(shí)例一直會(huì)持有該Activity的引用。從而導(dǎo)致Activity得資源不到正?;厥?/b>

(3)handler造成的內(nèi)存泄露

handler:線程之間發(fā)送消息異步的框架。而handler使我們開(kāi)發(fā)中造成內(nèi)存泄露最常見(jiàn)的。比如我們平常使用handler來(lái)處理一些網(wǎng)絡(luò)數(shù)據(jù)獲取的時(shí)候,會(huì)請(qǐng)求一個(gè)回調(diào),這時(shí)候我們借助一個(gè)handler來(lái)處理,如果這個(gè)時(shí)候你沒(méi)有考慮到handler引起的內(nèi)存泄露,就會(huì)造成嚴(yán)重問(wèn)題。例如:

? ? private Handler mhandler = new Handler(){

? ? ? ? @Override

? ? ? ? public void handleMessage(Message msg) {

? ? ? ? //...

? ? ? ? }

? ? };


? 上面的handler寫(xiě)法就會(huì)造成內(nèi)存泄露,因?yàn)槲覀冞@個(gè)mhandler它是handler的非靜態(tài)內(nèi)部類的使實(shí)例,所以他持有外部類的引用,我們知道handler的消息隊(duì)列是在一個(gè)looper線程中不斷的輪訓(xùn)處理消息,當(dāng)我們的Activity要退出的時(shí)候,消息對(duì)列中還有未處理的消息或正在處理的消息,而消息隊(duì)列中的msg他又持有了mhandler的實(shí)例引用,這就導(dǎo)致了Activity資源想回收的時(shí)候無(wú)法回收,從而造成內(nèi)存泄露。正確的寫(xiě)法如下:

public class TwoActivity extends Activity {

//? ? private Handler handler = new Handler(){

//? ? ? ? @Override

//? ? ? ? public void handleMessage(Message msg) {

//? ? ? ? ? ? //...

//? ? ? ? }

//? ? };

? ? private MyHandler myHandler = new MyHandler();

? ? private static class MyHandler extends Handler{

? ? ? ? private WeakReference<Context> reference;

? ? ? ? private MyHandler (Context context){

? ? ? ? ? ? reference = new WeakReference<Context>(context);

? ? ? ? }

? ? ? ? @Override

? ? ? ? public void handleMessage(Message msg) {

? ? ? ? ? ? super.handleMessage(msg);

? ? ? ? }

? ? }

? ? @Override

? ? protected void onCreate(Bundle savedInstanceState) {

? ? ? ? super.onCreate(savedInstanceState);

? ? ? ? setContentView(R.layout.activity_main);

? ? ? ? getdata();

? ? }

? ? private void getdata() {

? ? ? ? Message msg = Message.obtain();

? ? ? ? myHandler.sendMessage(msg);

? ? }

? ? @Override

? ? protected void onDestroy() {

? ? ? ? super.onDestroy();

? ? ? ? myHandler.removeCallbacksAndMessages(null);

? ? }

}

? 我們會(huì)把handler的內(nèi)部類,改為靜態(tài)的內(nèi)部類,同時(shí)我們會(huì)在MyHandler的內(nèi)部持有外部類的弱引用,這樣就能解決好內(nèi)存泄露問(wèn)題。

(4)盡量避免使用static變量。

如果我們把成員變量設(shè)置為了static,這個(gè)成員變量的生命周期就會(huì)和APP的生命周期是一致的,這樣就會(huì)導(dǎo)致一個(gè)問(wèn)題。如果你的APP進(jìn)程是常駐內(nèi)存的,即使APP切換到后臺(tái),這部分的stitic的變量他也是不會(huì)被釋放的,按照我們現(xiàn)在APP的內(nèi)存管理機(jī)制,占內(nèi)存較大的進(jìn)程將優(yōu)先被回收。所以說(shuō)當(dāng)你的進(jìn)程被回收了之后,你所存在的那些變量其實(shí)他的數(shù)據(jù)是不安全的。對(duì)于static造成的內(nèi)存泄露,解決的方法就是在類設(shè)計(jì)的時(shí)候,你要考慮好是不是初始化的時(shí)候去設(shè)為靜態(tài)成員,是否可以考慮一下懶加載,盡量避免static變量,如果必須要涉及到了static變量,那么一定要對(duì)這些變量的生命周期進(jìn)行管理起來(lái)。

(5)資源未關(guān)閉造成的內(nèi)存泄露,這個(gè)無(wú)需多說(shuō),大家都理解

6)AsyncTask

原因和handler差不多,但是有一點(diǎn)是不一樣的,我們可以在onDestory()方法中取消掉AsyncTask任務(wù),這樣就可以避免造成內(nèi)存泄露。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容