一娶视、Android性能優(yōu)化之內(nèi)存泄露

前言

性能優(yōu)化目的:
1.如何去優(yōu)化自己的項(xiàng)目睁宰,運(yùn)行更流暢柒傻。

現(xiàn)實(shí)App進(jìn)程分配內(nèi)存空間: 16M 32M 64M

2..以后開(kāi)發(fā)項(xiàng)目的時(shí)候就要從一開(kāi)始把項(xiàng)目做好

內(nèi)存泄露

什么是內(nèi)存泄露红符? 內(nèi)存不在GC管控之內(nèi)
當(dāng)一個(gè)對(duì)象已經(jīng)不需要再使用時(shí),本該被回收時(shí)致开,而有另外一個(gè)正在使用的對(duì)象持有它的引用從而導(dǎo)致對(duì)象不能被回收双戳。這種導(dǎo)致了本該被回收的對(duì)象不能被回收而停留在堆內(nèi)存中糜芳,就產(chǎn)生了內(nèi)存泄露峭竣。

不是所有指令都執(zhí)行得又快又好皆撩,下面介紹內(nèi)存及它如何影響系統(tǒng)運(yùn)行。普遍認(rèn)為沮榜,多數(shù)程序語(yǔ)言接近硬件或高性能喻粹,如C守呜、C++和Fortran,通常程序員會(huì)自己管理內(nèi)存弥喉,高手工程師對(duì)內(nèi)存的分配由境,會(huì)慎重處理虏杰,并在未來(lái)結(jié)束使用時(shí)再次分配勒虾,一旦確認(rèn)何時(shí)及怎樣分配內(nèi)存修然,內(nèi)存管理的品質(zhì)就依賴(lài)于工程師的技能跟效率。實(shí)際情況是工程師們玻靡,不都會(huì)去追蹤那零碎的內(nèi)存碎片中贝。程序開(kāi)發(fā)是個(gè)混亂又瘋狂的過(guò)程啃奴,內(nèi)存通常都沒(méi)辦法完全被釋放,這些被囚禁的內(nèi)存叫內(nèi)存泄露雄妥。

內(nèi)存泄露

內(nèi)存泄露占用了大量資源最蕾,這些資源其實(shí)可以更好地使用,為減少泄露引起的混亂老厌、負(fù)擔(dān)瘟则、甚至資金損失,便有了內(nèi)存管理語(yǔ)言枝秤。


跟蹤內(nèi)存分配

這些語(yǔ)言在運(yùn)行時(shí)跟蹤內(nèi)存分配,以便當(dāng)程序不再需要時(shí)釋放系統(tǒng)內(nèi)存淀弹,完全不用工程師親自操作丹壕,這些內(nèi)存回收藝術(shù)或科學(xué),在內(nèi)存管理環(huán)節(jié)下叫垃圾清理薇溃。這個(gè)設(shè)計(jì)概念在1959年菌赖,當(dāng)初為了解決lisp語(yǔ)言問(wèn)題,由John McCarthy發(fā)明的沐序。

垃圾清理的基本概念有:

第一琉用,找到未來(lái)無(wú)法存取的數(shù)據(jù)堕绩,例如所有不受指令操控的內(nèi)存。
第二邑时,回收被利用過(guò)的資源奴紧。原理簡(jiǎn)單,但是兩百萬(wàn)行編碼晶丘,跟4gigs的分配黍氮,在實(shí)際操作時(shí)卻非常困難。如果在程序中有20000個(gè)對(duì)象分配浅浮,垃圾清理會(huì)讓人困惑沫浆,哪一個(gè)是沒(méi)用的?或者脑题,何時(shí)啟動(dòng)垃圾清理釋放內(nèi)存件缸?這些問(wèn)題其實(shí)很復(fù)雜。好在50年來(lái)叔遂,我們找到了解決問(wèn)題的方法他炊,就是Android Runtime中的垃圾清理。比McCarthy最初的方法更高級(jí)已艰,速度快且是非侵入性的痊末。經(jīng)由分配類(lèi)型,及系統(tǒng)如何有效地組織分配以利GC的運(yùn)行哩掺,并作為新的配置凿叠。所有影響android runtime的內(nèi)存堆都被分割到空間中,根據(jù)這些特點(diǎn)嚼吞,哪些數(shù)據(jù)適合放到什么空間盒件,取決于哪個(gè)Android版本。

根據(jù)不同類(lèi)型進(jìn)行運(yùn)行時(shí)內(nèi)存的分配
了解內(nèi)存分配的幾種策略:

1.靜態(tài)的
在編譯時(shí)就能確定每個(gè)數(shù)據(jù)目標(biāo)在運(yùn)行時(shí)刻的存儲(chǔ)空間需求舱禽,因而在編譯時(shí)就可以給他們分配固定的內(nèi)存空間.這種分配策略要求程序代碼中不允許有可變代碼結(jié)構(gòu)(比如可變數(shù)組的存在)炒刁,也不允許有嵌套或者遞歸結(jié)構(gòu)的出現(xiàn),因?yàn)樗鼈兌紩?huì)導(dǎo)致編譯程序無(wú)法計(jì)算準(zhǔn)確的存儲(chǔ)空間需求誊稚。

靜態(tài)的存儲(chǔ)區(qū)翔始,內(nèi)存在分配的時(shí)候就已經(jīng)分配好了,這塊的內(nèi)存在程序在整個(gè)運(yùn)行期間內(nèi)一直存在里伯。

2.棧式的
棧式存儲(chǔ)分配也可稱(chēng)為動(dòng)態(tài)存儲(chǔ)分配,是由一個(gè)類(lèi)似于堆棧的運(yùn)行棧來(lái)實(shí)現(xiàn)的.和靜態(tài)存儲(chǔ)分配相反,在棧式存儲(chǔ)方案中,程序?qū)?shù)據(jù)區(qū)的需求在編譯時(shí)是完全未知的,只有到運(yùn)行的時(shí)候才能夠知道,但是規(guī)定在運(yùn)行中進(jìn)入一個(gè)程序模塊時(shí),必須知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能夠?yàn)槠浞峙鋬?nèi)存.和我們?cè)跀?shù)據(jù)結(jié)構(gòu)所熟知的棧一樣,棧式存儲(chǔ)分配按照先進(jìn)后出的原則進(jìn)行分配城瞎。

在執(zhí)行函數(shù)(方法)時(shí),函數(shù)一些內(nèi)部變量的存儲(chǔ)都可以放在棧上創(chuàng)建疾瓮,函數(shù)執(zhí)行結(jié)束的時(shí)候這些存儲(chǔ)單元就會(huì)自動(dòng)被釋放掉脖镀。棧內(nèi)存包括分配的運(yùn)算速度很快,因?yàn)閮?nèi)置在處理器里面的爷贫。當(dāng)然容量有限认然。

3.堆式的
堆式存儲(chǔ)分配則專(zhuān)門(mén)負(fù)責(zé)在編譯時(shí)或運(yùn)行時(shí)模塊入口處都無(wú)法確定存儲(chǔ)要求的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配,比如可變長(zhǎng)度串和對(duì)象實(shí)例.堆由大片的可利用塊或空閑塊組成,堆中的內(nèi)存可以按照任意順序分配和釋放.
在C/C++可能需要自己負(fù)責(zé)釋放(java里面直接額依賴(lài)GC機(jī)制)

棧式和堆式區(qū)別:
從堆和棧的功能和作用來(lái)通俗的比較,堆主要用來(lái)存放對(duì)象的补憾,棧主要是用來(lái)執(zhí)行程序的.
heap:是由malloc之類(lèi)函數(shù)分配的空間所在地漫萄。地址是由低向高增長(zhǎng)的卷员。
stack:是自動(dòng)分配變量,以及函數(shù)調(diào)用的時(shí)候所使用的一些空間腾务。地址是由高向低減少的毕骡。
使用棧就象我們?nèi)ワ堭^里吃飯,只管點(diǎn)菜(發(fā)出申請(qǐng))岩瘦、付錢(qián)未巫、和吃(使用),吃飽了就走启昧,不必理會(huì)切菜叙凡、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作密末,他的好處是快捷握爷,但是自由度小。
使用堆就象是自己動(dòng)手做喜歡吃的菜肴严里,比較麻煩新啼,但是比較符合自己的口味,而且自由度大刹碾。
public class Main{
    int a = 1;//堆里面
    Student s = new Student();//堆里面
    public void XXX(){//堆里面
        int b = 1;//棧里面
        Student s2 = new Student();
    }

}

1.成員變量全部存儲(chǔ)在堆中(包括基本數(shù)據(jù)類(lèi)型燥撞,引用和引用的對(duì)象實(shí)體) --- 因?yàn)樗鼈儗儆陬?lèi),類(lèi)最終還是要被new出來(lái)迷帜。
2.局部變量的基本數(shù)據(jù)類(lèi)型和引用存儲(chǔ)于棧當(dāng)中物舒,引用的對(duì)象實(shí)體存儲(chǔ)于在堆中。-----因?yàn)樗麄儗儆诜椒ó?dāng)中的變量戏锹,生命周期會(huì)隨著方法一起結(jié)束冠胯。

類(lèi)里面的對(duì)象(非局部變量)引用是存在堆里面

我們所討論的內(nèi)存泄漏,主要是討論堆存儲(chǔ)景用,它存放的是引用指向的對(duì)象實(shí)體涵叮。

有時(shí)候確實(shí)會(huì)有一種情況:當(dāng)需要的時(shí)候可以訪問(wèn),當(dāng)不需要的時(shí)候可以被回收也可以被暫時(shí)保存以備重復(fù)使用伞插。
比如:ListView或者GridView割粮、REcyclerView加載大量數(shù)據(jù)或者圖片的時(shí)候,
圖片非常占用內(nèi)存媚污,一定要管理好內(nèi)存舀瓢,不然很容易內(nèi)存溢出。
滑出去的圖片就回收耗美,節(jié)省內(nèi)存京髓『阶海看ListView的源碼----回收對(duì)象,還會(huì)重用ConvertView堰怨。
如果用戶(hù)反復(fù)滑動(dòng)或者下面還有同樣的圖片芥玉,就會(huì)造成多次重復(fù)IO(很耗時(shí)),
那么需要緩存---平衡好內(nèi)存大小和IO备图,算法和一些特殊的java類(lèi)灿巧。
算法:lrucache(最近最少使用先回收)
特殊的java類(lèi):利于回收,StrongReference揽涮,SoftReference抠藕,WeakReference,PhatomReference

StrongReference --- 強(qiáng)引用:
StrongReference 是 Java的默認(rèn)引用實(shí)現(xiàn), 它會(huì)盡可能長(zhǎng)時(shí)間的存活于 JVM 內(nèi)蒋困, 當(dāng)沒(méi)有任何對(duì)象指向它時(shí) GC 執(zhí)行后將會(huì)被回收
回收時(shí)機(jī):從不回收 使用:對(duì)象的一般保存 生命周期:JVM停止的時(shí)候才會(huì)終止

SoftReference --- 軟引用
SoftReference 于 WeakReference 的特性基本一致盾似, 最大的區(qū)別在于 SoftReference 會(huì)盡可能長(zhǎng)的保留引用直到 JVM 內(nèi)存不足時(shí)才會(huì)被回收(虛擬機(jī)保證), 這一特性使得 SoftReference 非常適合緩存應(yīng)用
回收時(shí)機(jī):當(dāng)內(nèi)存不足的時(shí)候;使用:SoftReference<String>結(jié)合ReferenceQueue構(gòu)造有效期短雪标;生命周期:內(nèi)存不足時(shí)終止

WeakReference --- 弱引用
WeakReference 是一個(gè)弱引用, 當(dāng)所引用的對(duì)象在 JVM 內(nèi)不再有強(qiáng)引用時(shí), GC 后 weak reference 將會(huì)被自動(dòng)回
回收時(shí)機(jī):在垃圾回收的時(shí)候零院;使用:同軟引用; 生命周期:GC后終止

PhatomReference --- 虛引用
“虛引用”顧名思義汰聋,就是形同虛設(shè)门粪,與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期烹困。如果一個(gè)對(duì)象僅>持有虛引用玄妈,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收髓梅。
虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)拟蜻。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用>隊(duì)列 (ReferenceQueue)聯(lián)合使用。當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí)枯饿,如果發(fā)現(xiàn)它還有虛引用酝锅,就會(huì)在回收對(duì)
象的內(nèi)存之前,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中奢方。
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);
程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入了虛引用搔扁,來(lái)了解被引用的對(duì)象是否將要被垃圾回收。如果程序發(fā)現(xiàn)某個(gè)虛引用已經(jīng)被加入到引用隊(duì)列蟋字,那么就可以在所引用的對(duì)象的內(nèi)存被回收之前采取必要的行動(dòng)稿蹲。
回收時(shí)機(jī):在垃圾回收的時(shí)候;使用:合ReferenceQueue來(lái)跟蹤對(duì)象被垃圾回收期回收的活動(dòng)鹊奖; 生命周期:GC后終止

開(kāi)發(fā)時(shí)苛聘,為了防止內(nèi)存溢出,處理一些比較占用內(nèi)存大并且生命周期長(zhǎng)的對(duì)象的時(shí)候,可以盡量使用軟引用和弱引用设哗。軟引用比LRU算法更加任性唱捣,回收量是比較大的,你無(wú)法控制回收哪些對(duì)象网梢。

比如使用場(chǎng)景:默認(rèn)頭像震缭、默認(rèn)圖標(biāo)。
ListView或者GridView澎粟、RecyclerView要使用內(nèi)部緩存+外部緩存(SD卡)

-----------------------------內(nèi)存泄漏例子--------------------------
單例模式導(dǎo)致內(nèi)存對(duì)象無(wú)法釋放而導(dǎo)致內(nèi)存泄漏

public class CommonUtils {

    private static CommonUtils instance;

    private Context context;

    private CommonUtils(Context context) {
        this.context = context;
    }

    public static CommonUtils getInstance(Context context) {

        if (instance == null) {
            instance = new CommonUtils(context);
        }
        return instance;
    }
}
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

       CommonUtils commonUtils =  CommonUtils.getInstance(this);


    }
}
運(yùn)行這個(gè)簡(jiǎn)單的單例模式程序蛀序,進(jìn)行多次橫豎屏切換欢瞪,發(fā)現(xiàn)可用內(nèi)存越來(lái)越小活烙,存在內(nèi)存泄漏現(xiàn)象,最終導(dǎo)致內(nèi)存溢出遣鼓。
內(nèi)存泄漏

分析原因:

點(diǎn)擊Monitors->Memory中 Dump Java Heap 采集記錄各個(gè)類(lèi)內(nèi)存使用情況啸盏,如下圖:
內(nèi)存泄漏原因
根據(jù)圖我們發(fā)現(xiàn),進(jìn)行多次橫豎屏切換時(shí)骑祟,生產(chǎn)了9個(gè)MainActivity回懦。多個(gè)MainActivity所占用的內(nèi)存資源沒(méi)有被GC及時(shí)回收,導(dǎo)致內(nèi)存泄漏次企。

總結(jié)
我們能用Application的context就用Application的
CommonUtils 生命周期是跟Application進(jìn)程同生同死怯晕。
特別感謝
動(dòng)腦學(xué)院Ricky

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市缸棵,隨后出現(xiàn)的幾起案子舟茶,更是在濱河造成了極大的恐慌,老刑警劉巖堵第,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吧凉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡踏志,警方通過(guò)查閱死者的電腦和手機(jī)阀捅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)针余,“玉大人饲鄙,你說(shuō)我怎么就攤上這事≡惭悖” “怎么了忍级?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)摸柄。 經(jīng)常有香客問(wèn)我颤练,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任嗦玖,我火速辦了婚禮患雇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宇挫。我一直安慰自己苛吱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布器瘪。 她就那樣靜靜地躺著翠储,像睡著了一般。 火紅的嫁衣襯著肌膚如雪橡疼。 梳的紋絲不亂的頭發(fā)上援所,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音欣除,去河邊找鬼住拭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛历帚,可吹牛的內(nèi)容都是我干的滔岳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挽牢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谱煤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起禽拔,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刘离,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后奏赘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寥闪,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年磨淌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疲憋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梁只,死狀恐怖缚柳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搪锣,我是刑警寧澤秋忙,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站构舟,受9級(jí)特大地震影響灰追,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一弹澎、第九天 我趴在偏房一處隱蔽的房頂上張望朴下。 院中可真熱鬧,春花似錦苦蒿、人聲如沸殴胧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)团滥。三九已至,卻和暖如春报强,著一層夾襖步出監(jiān)牢的瞬間灸姊,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工躺涝, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厨钻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓坚嗜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诗充。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苍蔬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容