1.內(nèi)存泄漏
- 內(nèi)存泄漏(Memory Leak)是指程序中己動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費黎炉,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
- 生活例子:打網(wǎng)球醋拧,打飛了的球沒去撿或飛的太遠(yuǎn)撿不回來了
- 內(nèi)存泄漏是導(dǎo)致內(nèi)存溢出的原因之一;內(nèi)存泄漏累積起來就會造成內(nèi)存溢出
- 內(nèi)存泄漏可以通過完善代碼來避免
- 一般我們所說的內(nèi)存泄漏指的是堆內(nèi)存的泄露,堆內(nèi)存是指程序從堆中分配的,大小隨機(jī)的用完后必須顯示釋放的內(nèi)存丹壕,C++/C中有free函數(shù)可以釋放內(nèi)存庆械,java中有垃圾回收機(jī)制不用程序員自己手動調(diào)用釋放
2.內(nèi)存溢出
- 內(nèi)存溢出(Out Of Memory,簡稱OOM)是指應(yīng)用系統(tǒng)中存在無法回收的內(nèi)存或使用的內(nèi)存過多菌赖,最終使得程序運行要用到的內(nèi)存大于能提供的最大內(nèi)存缭乘。
- 生活例子 : 水杯滿了還往里面加水
- 內(nèi)存溢出可以通過調(diào)整配置來減少發(fā)生頻率,無法徹底避免
3.內(nèi)存泄漏的原因:
以發(fā)生的方式來分類,內(nèi)存泄漏可以分為4類:
- 常發(fā)性內(nèi)存泄漏 : 發(fā)生內(nèi)存泄漏的代碼會被多次執(zhí)行到,每次被執(zhí)行的時候都會導(dǎo)致一塊內(nèi)存泄漏琉用。
- 偶發(fā)性內(nèi)存泄漏 : 發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過程下才會發(fā)生堕绩。常發(fā)性和偶發(fā)性是相對的。對于特定的環(huán)境邑时,偶發(fā)性的也許就變成了常發(fā)性的奴紧。所以測試環(huán)境和測試方法對檢測內(nèi)存泄漏至關(guān)重要。
- 一次性內(nèi)存泄漏 : 發(fā)生內(nèi)存泄漏的代碼只會被執(zhí)行一次晶丘,或者由于算法上的缺陷黍氮,導(dǎo)致總會有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如浅浮,在類的構(gòu)造函數(shù)中分配內(nèi)存沫浆,在析構(gòu)函數(shù)中卻沒有釋放該內(nèi)存,所以內(nèi)存泄漏只會發(fā)生一次滚秩。
- 隱式內(nèi)存泄漏 : 程序在運行過程中不停的分配內(nèi)存专执,但是直到結(jié)束的時候才釋放內(nèi)存。嚴(yán)格的說這里并沒有發(fā)生內(nèi)存泄漏郁油,因為最終程序釋放了所有申請的內(nèi)存本股。但是對于一個服務(wù)器程序,需要運行幾天已艰,幾周甚至幾個月痊末,不及時釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存。所以哩掺,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏凿叠。(重視)
總結(jié):從用戶使用程序的角度來看,內(nèi)存泄漏本身不會產(chǎn)生什么危害嚼吞,作為一般的用戶盒件,根本感覺不到內(nèi)存泄漏的存在。真正有危害的是內(nèi)存泄漏的堆積舱禽,這會最終消耗盡系統(tǒng)所有的內(nèi)存炒刁。從這個角度來說,一次性內(nèi)存泄漏并沒有什么危害誊稚,因為它不會堆積翔始,而隱式內(nèi)存泄漏危害性則非常大,因為較之于常發(fā)性和偶發(fā)性內(nèi)存泄漏它更難被檢測到城瞎。
4.內(nèi)存溢出的原因及解決方法:
內(nèi)存溢出原因:
- 內(nèi)存中加載的數(shù)據(jù)量過于龐大,如一次從數(shù)據(jù)庫取出過多數(shù)據(jù)飒箭;
- 集合類中有對對象的引用蜒灰,使用完后未清空,使得JVM不能回收强窖;
- 代碼中存在死循環(huán)或循環(huán)產(chǎn)生過多重復(fù)的對象實體;
- 使用的第三方軟件中的BUG削饵;
- 啟動參數(shù)內(nèi)存值設(shè)定的過小
內(nèi)存溢出的解決方法: - 修改JVM啟動參數(shù)未巫,直接增加內(nèi)存窿撬。(-Xms,-Xmx參數(shù)一定不要忘記加叙凡。)
- 檢查錯誤日志劈伴,查看“OutOfMemory”錯誤前是否有其 它異常或錯誤握爷。
- 對代碼進(jìn)行走查和分析跛璧,找出可能發(fā)生內(nèi)存溢出的位置。
- 使用內(nèi)存查看工具動態(tài)查看內(nèi)存使用情況新啼。
重點排查以下幾點:
1追城、檢查對數(shù)據(jù)庫查詢中,是否有一次獲得全部數(shù)據(jù)的查詢燥撞。一般來說座柱,如果一次取十萬條記錄到內(nèi)存,就可能引起內(nèi)存溢出物舒。這個問題比較隱蔽色洞,在上線前,數(shù)據(jù)庫中數(shù)據(jù)較少冠胯,不容易出問題火诸,上線后,數(shù)據(jù)庫中數(shù)據(jù)多了荠察,一次查詢就有可能引起內(nèi)存溢出置蜀。因此對于數(shù)據(jù)庫查詢盡量采用分頁的方式查詢奈搜。
2、檢查代碼中是否有死循環(huán)或遞歸調(diào)用盯荤。
3媚污、檢查是否有大循環(huán)重復(fù)產(chǎn)生新對象實體。
4廷雅、檢查List航缀、MAP等集合對象是否有使用完后芥玉,未清除的問題。List揽涮、MAP等集合對象會始終存有對對象的引用盾似,使得這些對象不能被GC回收零院。
5.如何避免內(nèi)存泄漏?
1嵌牺、在涉及使用Context時拟蜻,對于生命周期比Activity長的對象應(yīng)該使用Application的Context酝锅。凡是使用Context優(yōu)先考慮Application的Context爸舒,當(dāng)然它并不是萬能的扭勉,對于有些地方則必須使用Activity的Context液走。對于Application,Service网梢,Activity三者的Context的應(yīng)用場景如下:
其中拣宰,NO1表示Application和Service可以啟動一個Activity巡社,不過需要創(chuàng)建一個新的task任務(wù)隊列重贺。而對于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)存泄漏:
- 將內(nèi)部類改為靜態(tài)內(nèi)部類
- 靜態(tài)內(nèi)部類中使用弱引用來引用外部類的成員變量
6.如何檢查和分析內(nèi)存泄漏堵幽?
因為內(nèi)存泄漏是在堆內(nèi)存中朴下,所以對我們來說并不是可見的殴胧。通常我們可以借助MAT、LeakCanary等工具來檢測應(yīng)用程序是否存在內(nèi)存泄漏灸姊。
1力惯、MAT是一款強大的內(nèi)存分析工具,功能繁多而復(fù)雜诱建。
2俺猿、LeakCanary則是由Square開源的一款輕量級的第三方內(nèi)存泄漏檢測工具,當(dāng)檢測到程序中產(chǎn)生內(nèi)存泄漏時谊惭,它將以最直觀的方式告訴我們哪里產(chǎn)生了內(nèi)存泄漏和導(dǎo)致誰泄漏了而不能被回收豹芯。