本系列文章斤斧,主要是總結我對Android開發(fā)過程中內(nèi)存優(yōu)化的理解早抠,很多東西都是平常的習慣和一些細節(jié)問題,重在剖析優(yōu)化的原理撬讽,養(yǎng)成一種良好的代碼習慣蕊连。
概述
既然談優(yōu)化,就繞不開Android三個內(nèi)存相關的經(jīng)典問題:
- OOM
- 內(nèi)存泄漏
- 頻繁GC卡頓
導致這三個問題的原因:
OOM
App在啟動時會從系統(tǒng)分配一個默認的堆內(nèi)存游昼,同時擁有一個堆內(nèi)存最大值(可以動態(tài)申請這個大懈什浴),這個Max Heap Size的大小烘豌,決定了軟件運行時可以申請的最大運行內(nèi)存载庭。App軟件內(nèi)存分配是個不斷創(chuàng)建和GC回收的過程,就像一個水池擁有注入和排出水的通道廊佩,當注入過快囚聚,排出不足時,水池滿了溢出标锄,Out of Memory顽铸,即我們常說的OOM。
內(nèi)存泄漏
當我們在代碼中創(chuàng)建對象料皇,會申請內(nèi)存空間谓松,同時包含一個對象的引用,當我們長時間不使用該引用時践剂,JVM GC操作時會根據(jù)這個引用去釋放內(nèi)存鬼譬。但是,對象的回收可能有點差錯逊脯,如果這個對象A被另一個線程B所引用优质,當我們不再使用A,可A卻處于B的hold狀態(tài)男窟,那么我們每次創(chuàng)建的A都得不到回收盆赤,這個時候就會發(fā)生內(nèi)存泄漏了贾富。
頻繁GC卡頓
上面說了歉眷,App的堆內(nèi)存有最大值牺六,是有限的,那么如果我們頻繁的創(chuàng)建汗捡,當運行內(nèi)存不斷上升淑际,為了維持App的運行,GC回收也會頻繁操作扇住,軟件運行資源有些春缕,必然導致卡頓問題。
JAVA的GC機制艘蹋,非常的復雜和精辟锄贼,不可一言概論之,在看過許多blog之后女阀,給出一點自己的總結宅荤。
簡述JVM GC
我們都知道Java語言非常的方便,不像C語言浸策,申請和釋放內(nèi)存都是自己操作冯键,java有虛擬機幫忙。Android 的每個應用程序都會使用一個專有的Dalvik虛擬機實例來運行庸汗,即使內(nèi)存泄漏也只是kill當前App.
Java虛擬機有一套完整的GC方案惫确,只是簡單理解的話就是,它維持著一個對象關系樹蚯舱,當開始GC操作時改化,它會從GC Roots開始掃描整個Object Tree,當發(fā)現(xiàn)某個無法從Tree中引用到的對象時枉昏,便將其回收陈肛。
GC Roots分類舉例:
- Class類
- Alive Thread
- 線程stack上的對象,如方法或者局部變量
- JNI活動對象
- System Class Loader
Java中的引用關系
java中有四種對象引用關系凶掰,分別是:強引用StrongRefernce燥爷、軟引用SoftReference、弱引用WeakReference懦窘、虛引用PhantomReference前翎,這四種引用關系分別對應的效果:
StrongRefernce
通過new創(chuàng)建的對象,如Object obj = new Object();畅涂,強引用不會被垃圾回收器回收和銷毀港华,即是OOM,所以這也容易造成我們接下來會分析的《非靜態(tài)內(nèi)部類持有對象導致的內(nèi)存泄漏問題》
SoftReference
軟引用可以被垃圾回收器回收午衰,但它的生命周期要強于弱引用立宜,但GC回收發(fā)生時冒萄,只有在內(nèi)存空間不足時才會回收它
WeakReference
弱引用的生命周期短,可以被GC回收橙数,但GC回收發(fā)生時尊流,掃描到弱引用便會被垃圾回收和銷毀掉
PhantomReference
虛引用任何時候都可以被GC回收,它不會影響對象的垃圾回收機制灯帮,它只有一個構造函數(shù)崖技,因此只能配合ReferenceQueue一起使用,用于記錄對象回收的過程
PhantomReference(T referent, ReferenceQueue<? super T> q)
關于ReferenceQueue
他的作用主要用于記錄引用是否被回收钟哥,除了強引用其他的引用方式得構造函數(shù)中都包含了ReferenceQueue參數(shù)迎献。當調(diào)用引用的get()方法返回null時,我們的對象不一定已經(jīng)回收掉了腻贰,可能正在進入回收流程中吁恍,而當對象被確認回收后,它的引用會被添加到ReferenceQueue中播演。
Felix obj = new Felix();
ReferenceQueue<Felix> rQueue = new ReferenceQueue<Felix>();
WeakReference<Felix> weakR = new WeakReference<Felix>(obj,rQueue);
總結
看完Android引用和回收機制冀瓦,我們對于代碼中內(nèi)存問題的原因也有一定認識,當時現(xiàn)實中內(nèi)存泄漏或者溢出的問題宾巍,總是不經(jīng)意間咕幻,在我之后一些列的文章中,會對不同場景的代碼問題進行分析和解決顶霞,一起來關注吧肄程!