內(nèi)存管理基礎(chǔ)
Java內(nèi)存分配模型
Java的對(duì)象生命周期
1.創(chuàng)建
為對(duì)象分配內(nèi)存空間 構(gòu)造對(duì)象
2.應(yīng)用
此時(shí) 對(duì)象至少被一個(gè)強(qiáng)引用持有
3.不可見(jiàn)階段
沒(méi)有強(qiáng)引用优构,沒(méi)有任何引用了渊涝, 對(duì)象此時(shí)還是存在內(nèi)存里面的,如果被gc掃描到了,就會(huì)被后面的可達(dá)性分析劲绪,如果不可達(dá)了就會(huì)被回收
4.不可達(dá)
5.收集
6.終結(jié) 垃圾回收
7.對(duì)象空間重新分配
對(duì)象的內(nèi)存布局
可回收對(duì)象的判定
Java的四種引用
- 1.強(qiáng)引用(Strong Reference)
在代碼中普遍使用的爷抓,類(lèi)似Person person = new Person();如果一個(gè)對(duì)象具有強(qiáng)引用,則無(wú)論在什么情況下步淹,GC都不會(huì)回收被引用的對(duì)象从隆。當(dāng)內(nèi)存空間不足時(shí)诚撵,JAVA虛擬機(jī)寧可拋出OutOfMemoryError終止應(yīng)用程序也不會(huì)回收具有強(qiáng)引用的對(duì)象。
2.軟引用(Soft Reference)
表示一個(gè)對(duì)象處在有用但非必須的狀態(tài)键闺。如果一個(gè)對(duì)象具有軟引用寿烟,在內(nèi)存空間充足時(shí),GC就不會(huì)回收該對(duì)象辛燥,當(dāng)內(nèi)存空間不足時(shí)筛武,GC會(huì)回收該對(duì)象的內(nèi)存(回收發(fā)生在OutOfMemoryError之前)
軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對(duì)象被GC回收挎塌,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中徘六,以便在恰當(dāng)?shù)臅r(shí)候?qū)⒃撥浺没厥眨怯捎贕C線(xiàn)程的優(yōu)先級(jí)較低勃蜘,通常手動(dòng)調(diào)用System.gc()并不能立即執(zhí)行GC硕噩,因此弱引用所引用的對(duì)象并不一定會(huì)被馬上回收。
3.弱引用(Weak Reference)
用來(lái)描述非必須的對(duì)象缭贡,它類(lèi)似軟引用炉擅,但是強(qiáng)度比軟引用更弱一些,弱引用具有更短的生命阳惹,GC在掃描的過(guò)程中谍失,一旦發(fā)現(xiàn)只具有被弱引用關(guān)聯(lián)的對(duì)象,都會(huì)回收掉被弱引用關(guān)聯(lián)的對(duì)象莹汤,無(wú)論當(dāng)前內(nèi)存是否緊缺快鱼,GC都將回收被弱引用關(guān)聯(lián)的對(duì)象。
4.虛引用(Phantom Reference)
虛引用等同于沒(méi)有引用纲岭,這意味著在任何時(shí)候都可能被GC回收抹竹,設(shè)置虛引用的目的是為了被虛引用關(guān)聯(lián)的對(duì)象在被垃圾回收器時(shí),能夠收到一個(gè)系統(tǒng)通知止潮。(被用來(lái)跟蹤對(duì)象被GC回收的活動(dòng))虛引用和弱引用的區(qū)別在于窃判,虛引用在使用時(shí)必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用,其在GC回收期間的活動(dòng)如下:
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(object.quque);
也即是GC在回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)該對(duì)象具有虛引用,具壮,那么在回收之前會(huì)首先該對(duì)象的虛引用加入到與之關(guān)聯(lián)的引用隊(duì)列中,程序可以通過(guò)判斷引用隊(duì)列中是否已經(jīng)加入虛引用來(lái)了解被引用的對(duì)象是否被GC回收崭别。
GC垃圾回收算法
-
標(biāo)記清除算法
特點(diǎn)
位置不連續(xù),產(chǎn)生碎片
效率略低
兩遍掃描
image.jpeg
會(huì)掃描兩遍,第一遍哪些對(duì)象是存活的,哪些是沒(méi)使用的內(nèi)存空間逗旁,哪些是可回收的,標(biāo)記出來(lái)舆瘪,第二次掃描之后就把可回收的全部給回收掉
缺點(diǎn):多次清除后痢艺,內(nèi)存就會(huì)千瘡百孔仓洼,產(chǎn)生大量的內(nèi)存碎片
- 標(biāo)記整理算法
特點(diǎn)
沒(méi)有內(nèi)存碎片
效率偏低
兩遍掃描、指針需要調(diào)整
- 復(fù)制算法
特點(diǎn)
位置不連續(xù)堤舒,產(chǎn)生碎片
效率略低
利用率只有一半
分代收集算法 綜合運(yùn)用
新生代 采用復(fù)制算法
老年代 對(duì)象存活久 不容易產(chǎn)生垃圾 標(biāo)記整理算法,雖然效率慢點(diǎn)哺呜,回收頻率不需要那么高舌缤。
App內(nèi)存組成以及限制
Android 給每個(gè)App分配一個(gè)VM,讓App運(yùn)行在dalvik上某残,這樣即使App崩潰也不會(huì)影響到系統(tǒng)国撵,系統(tǒng)給VM分配了一定的內(nèi)存大小,App可以申請(qǐng)使用的內(nèi)存大小不能超過(guò)此硬性邏輯限制玻墅,就算物理內(nèi)存富余介牙,如果應(yīng)用超出VM最大內(nèi)存,就會(huì)出現(xiàn)內(nèi)存溢出crash澳厢。
由程序控制操作的內(nèi)存空間在heap上环础,分 java heapsize 和native heapsize
Java申請(qǐng)的內(nèi)存在vm heap上,所以如果java申請(qǐng)的內(nèi)存大小超過(guò)VM的邏輯內(nèi)存限制剩拢,就會(huì)出現(xiàn)內(nèi)存溢出的異常线得。
native層內(nèi)存申請(qǐng)不受其限制,native層收native process 對(duì)內(nèi)存大小的限制
通過(guò)代碼獲取內(nèi)存大小的限制
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE) activityManager.getMemoryClass();//以m為單位
Android內(nèi)存分配與回收機(jī)制
- 內(nèi)存分配
Android的Heap空間是一個(gè) Generational Heap Memory 的模型徐伐,最近分配的對(duì)象會(huì)存放在Young Generation區(qū)域贯钩,當(dāng)一個(gè)對(duì)象在這個(gè)區(qū)域停留的時(shí)間達(dá)到一定程度,它會(huì)被移動(dòng)到old Generation办素,最后累積一定時(shí)間再移動(dòng)到Permanent Generation區(qū)域角雷。
1、Young Generation
由一個(gè)Eden區(qū)和兩個(gè)Survivor區(qū)組成性穿,程序中生成的大部分新的對(duì)象都在Eden區(qū)中勺三,當(dāng)Eden區(qū)滿(mǎn)時(shí),還存活的對(duì)象將被復(fù)制到其中一個(gè)Survivor區(qū)季二,當(dāng)Survivor區(qū)滿(mǎn)時(shí)檩咱,此區(qū)存活的對(duì)象又被復(fù)制到另一個(gè)Survivor區(qū),當(dāng)這個(gè)Survivor區(qū)也滿(mǎn)時(shí)胯舷,會(huì)將其中存活的對(duì)象復(fù)制到老年代刻蚯。
2、Old Generation
一般情況下桑嘶,老年代中的對(duì)象生命周期都比較長(zhǎng)炊汹。
3、Permanent Generation
用于存放靜態(tài)的類(lèi)和方法逃顶,持久代對(duì)垃圾回收沒(méi)有顯著影響讨便。
總結(jié):內(nèi)存對(duì)象的處理過(guò)程如下
- 1.對(duì)象創(chuàng)建后在Eden區(qū)
2.執(zhí)行GC后充甚,如果對(duì)象仍然存活,則復(fù)制到SO區(qū)
3.當(dāng)SO區(qū)滿(mǎn)時(shí)霸褒,該區(qū)域存活對(duì)象將復(fù)制到S1區(qū)伴找,然后SO清空,接下來(lái)SO和S1角色互換废菱。
4.當(dāng)?shù)?步達(dá)到一定次數(shù)(系統(tǒng)版本不同會(huì)有差異)后技矮,存活對(duì)象將被復(fù)制到Old Generation。
5.當(dāng)這個(gè)對(duì)象再Old Generation區(qū)域停留的時(shí)間達(dá)到一定程度時(shí)殊轴,它會(huì)被移動(dòng)到Old Generation衰倦,最后累積一定時(shí)間再移動(dòng)到Permanent Generation區(qū)域
系統(tǒng)在Young Generation、Old Generation上采用不同的回收機(jī)制旁理,每一個(gè)Generation的內(nèi)存區(qū)域都有固定的大小樊零,隨著新的對(duì)象陸續(xù)被分配到此區(qū)域,當(dāng)對(duì)象總的大小臨近這一級(jí)別內(nèi)存區(qū)域的閥值時(shí)孽文,會(huì)觸發(fā)GC操作驻襟,以便騰出空間來(lái)存放其它新的對(duì)象。
執(zhí)行GC占用的時(shí)間與Generation和Generation中的對(duì)象數(shù)量有關(guān):
Young Generation < Old Generation < Permanent Generation
Gener中的對(duì)象數(shù)量與執(zhí)行時(shí)間成正比
4叛溢、Young Generation GC
由于其對(duì)象存活時(shí)間短塑悼,因此基于Copying算法(掃描出存活的對(duì)象,并復(fù)制到一塊新的完全未使用的控件中)來(lái)回收楷掉,新生代采用空閑指針的方式來(lái)控制GC觸發(fā)厢蒜,指針保持最后一個(gè)分配的對(duì)象在Young Generation區(qū)間的位置,當(dāng)有新的對(duì)象要分配內(nèi)存時(shí)烹植,用于檢測(cè)空間是否足夠斑鸦,不夠就觸發(fā)GC。
5草雕、Old Generation GC
由于其對(duì)象存活時(shí)間較長(zhǎng)巷屿,比較穩(wěn)定,因此采用Mart(標(biāo)記)算法(掃描出存活的對(duì)象墩虹,然后再回收未標(biāo)記的對(duì)象嘱巾,回收后對(duì)空出的空間要么合并,要么標(biāo)記出來(lái)便于下次分配诫钓,以減少內(nèi)存碎片帶來(lái)的效率損耗)來(lái)回收旬昭。
GC類(lèi)型
在Android系統(tǒng)中,GC有三種類(lèi)型:
kGcCauseForAlloc:分配內(nèi)存不夠引起的GC菌湃,會(huì)Stop World问拘,由于是并發(fā)GC,其它線(xiàn)程都會(huì)停止,知道GC完成骤坐。
kGcCauseBackground:內(nèi)存達(dá)到一定閾值觸發(fā)的GC绪杏,由于是一個(gè)后臺(tái)GC,所以不會(huì)引起Stop World纽绍。
kGcCauseExplicit:顯示調(diào)用時(shí)進(jìn)行的GC蕾久,當(dāng)ART打開(kāi)這個(gè)選項(xiàng)時(shí),使用System.gc時(shí)會(huì)進(jìn)行GC拌夏。
可達(dá)性分析與GCRoots
在Java腔彰,可作為GC Roots的對(duì)象包括:
1.方法區(qū):類(lèi)靜態(tài)屬性引用的對(duì)象;
2.方法區(qū):常量引用的對(duì)象
3.虛擬機(jī)棧(本地變量表)中引用的對(duì)象
4.本地方法棧JNI(Native方法)中引用的對(duì)象
Android低內(nèi)存殺進(jìn)程機(jī)制
低內(nèi)存終止守護(hù)進(jìn)程(LMK)
LMK 使用一個(gè)名為 oom_adj_score 的“內(nèi)存不足”分值來(lái)確定正在運(yùn)行的進(jìn)程的優(yōu)先級(jí)辖佣,以此決定要終止的進(jìn)程。最高得分的進(jìn)程最先被終止搓逾。后臺(tái)應(yīng)用最先被終止卷谈,系統(tǒng)進(jìn)程最后被終止。下表列出了從高到低的 LMK 評(píng)分類(lèi)別霞篡。評(píng)分最高的類(lèi)別世蔗,即第一行中的項(xiàng)目將最先被終止
-
Android 進(jìn)程,高分在上朗兵,低分在下
后臺(tái)應(yīng)用:之前運(yùn)行過(guò)且當(dāng)前不處于活動(dòng)狀態(tài)的應(yīng)用污淋,LMK將首先從具有最高oom_adj_score的應(yīng)用終止后臺(tái)應(yīng)用。
上一個(gè)應(yīng)用:最近用過(guò)的后臺(tái)應(yīng)用余掖,上一個(gè)應(yīng)用比后臺(tái)應(yīng)用具有更高的優(yōu)先級(jí)(得分更低)寸爆,因?yàn)橄啾饶硞€(gè)后臺(tái)應(yīng)用,用戶(hù)更有可能切換到上一個(gè)應(yīng)用盐欺。
主屏幕應(yīng)用:這是啟動(dòng)器應(yīng)用赁豆,終止該應(yīng)用會(huì)使壁紙消失。
服務(wù):服務(wù)由應(yīng)用啟動(dòng)冗美,可能包括同步或上傳到云端魔种。
可覺(jué)察的應(yīng)用,用戶(hù)可通過(guò)某種方式察覺(jué)到的非前臺(tái)應(yīng)用粉洼,例如運(yùn)行一個(gè)顯示小界面的搜索進(jìn)程或聽(tīng)音樂(lè)节预。
前臺(tái)應(yīng)用:當(dāng)前正在使用的應(yīng)用,終止前臺(tái)應(yīng)用看起來(lái)就像應(yīng)用崩潰了属韧,可能會(huì)向用戶(hù)提示設(shè)備出了問(wèn)題安拟。
持久性(服務(wù)):這些是設(shè)備的核心服務(wù),手機(jī)可能看起來(lái)即將重新啟動(dòng)挫剑。
原生:系統(tǒng)使用的極低級(jí)別的進(jìn)程
設(shè)備制造商可以更改LMK的行為去扣。
系統(tǒng)需要進(jìn)行內(nèi)存回收時(shí)最先回收空進(jìn)程,然后是后臺(tái)進(jìn)程,以此類(lèi)推最后才會(huì)回收前臺(tái)進(jìn)程(一般情況 下前臺(tái)進(jìn)程就是與用戶(hù)交互的進(jìn)程了,如果連前臺(tái)進(jìn)程都需要回收那么此時(shí)系統(tǒng)幾乎不可用了
image.jpeg
ActivityManagerService會(huì)對(duì)所有進(jìn)程進(jìn)行評(píng)分(存放在變量adj中),然后再講這個(gè)評(píng)分更新到內(nèi)核,由內(nèi)核去完成真正的內(nèi)存回收(lowmemorykiller,oom_killer),這里只是大概的流程愉棱,中間過(guò)程還是很復(fù)雜的唆铐。
什么是OOM
OOM(OutOfMemoryError)內(nèi)存溢出錯(cuò)誤,在常見(jiàn)的Crash疑難排行榜上奔滑,OOM絕對(duì)可以名列前茅并且經(jīng)久不衰艾岂,因?yàn)樗l(fā)生時(shí)的Crash堆棧信息往往不是導(dǎo)致問(wèn)題的根本原因,而只是壓死駱駝的最后一根稻草朋其。
Android 2.x系統(tǒng) GC LOG中的dalvik allocated + external allocated + 新分配的大小 >= getMemoryClass()值的時(shí)候就會(huì)發(fā)生OOM王浴。 例如,假設(shè)有這么一段Dalvik輸出的GC LOG: GC_FOR_MALLOC free 2K, 13% free 32586K/37455K, external 8989K/10356K, paused 20ms梅猿, 那么32586+8989+(新分配23975)=65550>64M時(shí)氓辣,就會(huì)發(fā)生OOM。
Android 4.x系統(tǒng) Android 4.x的系統(tǒng)廢除了external的計(jì)數(shù)器袱蚓,類(lèi)似bitmap的分配改到dalvik的 java heap中申請(qǐng)钞啸,只要allocated + 新分配的內(nèi)存 >= getMemoryClass()的時(shí)候就會(huì)發(fā)生OOM
OOM原因分類(lèi)
OOM代碼分析
Android虛擬機(jī)最終拋出OutOfMemoryError的地方
/art/runtime/thread.cc
void Thread::ThrowOutOfMemoryError(const char* msg) { LOG(WARNING) << StringPrintf("Throwing OutOfMemoryError "%s"%s",
msg, (tls32_.throwing_OutOfMemoryError ? " (recursive case)" : "")); if (!tls32_.throwing_OutOfMemoryError) {
tls32_.throwing_OutOfMemoryError = true; ThrowNewException("Ljava/lang/OutOfMemoryError;", msg); tls32_.throwing_OutOfMemoryError = false;
} else {
Dump(LOG_STREAM(WARNING)); // The pre-allocated OOME has no stack, so
help out and log one.
SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); }
}
堆內(nèi)存分配失敗
/art/runtime/gc/heap.cc
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
// If we're in a stack overflow, do not create a new exception. It would require running the
// constructor, which will of course still be in a stack overflow.
if (self->IsHandlingStackOverflow()) { self->SetException(
Runtime::Current()- >GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
return; }
std::ostringstream oss; size_t total_bytes_free = GetFreeMemory();
//為對(duì)象分配內(nèi)存時(shí)達(dá)到進(jìn)程的內(nèi)存上限
oss << "Failed to allocate a " << byte_count << " byte allocation with "
<< total_bytes_free << " free bytes and " << PrettySize(GetFreeMemoryUntilOOME()) << "
until OOM," << " target footprint " <<
target_footprint_.load(std::memory_order_relaxed) << ", growth limit "
<< growth_limit_;
//沒(méi)有足夠大小的連續(xù)地址空間
// There is no fragmentation info to log for large-object space.
if (allocator_type != kAllocatorTypeLOS) { CHECK(space != nullptr) << "allocator_type:" << allocator_type
<< " byte_count:" << byte_count
<< " total_bytes_free:" << total_bytes_free; space->LogFragmentationAllocFailure(oss, byte_count);
} }
創(chuàng)建線(xiàn)程失敗
/art/runtime/thread.cc
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
// TODO: remove from thread group?
env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
{
std::string msg(child_jni_env_ext.get() == nullptr ?
StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) : StringPrintf("pthread_create (%s stack) failed: %s",
PrettySize(stack_size).c_str(), strerror(pthread_create_result)));
ScopedObjectAccess soa(env);
soa.Self()->ThrowOutOfMemoryError(msg.c_str()); }
內(nèi)存三大問(wèn)題
- 1.內(nèi)存抖動(dòng)
短時(shí)間內(nèi)有大量對(duì)象創(chuàng)建和銷(xiāo)毀,它伴隨著頻繁的GC喇潘,內(nèi)存波動(dòng)圖形呈齒裝体斩、GC導(dǎo)致卡頓
例如在自定義view里面onDraw方法中多次創(chuàng)建對(duì)象
如何預(yù)防?
1)避免在循環(huán)中創(chuàng)建對(duì)象
2)避免在頻繁調(diào)用的方法中創(chuàng)建對(duì)象颖低,如view的onDraw方法
3)允許復(fù)用的情況下絮吵,使用對(duì)象池進(jìn)行緩存,如:Handler的Message單鏈表(obtain),
Message.obtain();
2.內(nèi)存溢出
內(nèi)存不夠用了忱屑,垃圾回收又回收不了
3.內(nèi)存泄漏
這個(gè)對(duì)象已經(jīng)不再使用了蹬敲,但是還被GC Roots引用,導(dǎo)致不能回收想幻。
程序中已動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放粱栖,造成系統(tǒng)內(nèi)存的浪費(fèi)。
長(zhǎng)生命周期對(duì)象持有短生命周期對(duì)象的強(qiáng)引用脏毯,從而導(dǎo)致短生命周期對(duì)象無(wú)法被回收
Android內(nèi)存泄漏常見(jiàn)場(chǎng)景以及解決方案
- 1.資源性對(duì)象未關(guān)閉
對(duì)于資源性對(duì)象不再使用時(shí)闹究,應(yīng)該立即調(diào)用它的close()函數(shù),將其關(guān)閉食店,然后再置為null渣淤,例如Bitmap等資源未關(guān)閉會(huì)造成內(nèi)存泄漏,此時(shí)我們應(yīng)該在Activity銷(xiāo)毀時(shí)及時(shí)關(guān)閉
2.注冊(cè)對(duì)象未注銷(xiāo)
例如BroadcastReceiver吉嫩、EventBus未注銷(xiāo)造成的內(nèi)存泄漏价认,我們應(yīng)該在Activity銷(xiāo)毀時(shí)及時(shí)注銷(xiāo)
3.類(lèi)的靜態(tài)變量持有大數(shù)據(jù)對(duì)象
盡量避免使用靜態(tài)變量存儲(chǔ)數(shù)據(jù),特別是大數(shù)據(jù)對(duì)象自娩,建議使用數(shù)據(jù)庫(kù)存儲(chǔ)
4.單例造成的內(nèi)存泄漏
優(yōu)先使用Application的Context用踩,如需使用Activity的Context,可以在傳入Context時(shí)使用弱引用進(jìn)行封裝,然后脐彩,在使用到的地方從弱引用中獲取Context碎乃,如果獲取不到,則直接return即可惠奸。
5.非靜態(tài)內(nèi)部類(lèi)的靜態(tài)實(shí)例
該實(shí)例的生命周期和應(yīng)用一樣長(zhǎng)梅誓,這就導(dǎo)致該靜態(tài)實(shí)例一直持有該Activity的引用,Activity的內(nèi)存資源不能正撤鹉希回收梗掰。此時(shí),我們可以將該內(nèi)部類(lèi)設(shè)為靜態(tài)內(nèi)部類(lèi)或?qū)?nèi)部類(lèi)抽取出來(lái)封裝成一個(gè)單例嗅回,如果需要使用Context及穗,盡量使用Application Context,如果需要使用Activity Context,就記得用完后置空讓GC可以回收绵载,否則還是會(huì)內(nèi)存泄漏拥坛。
6、Handler臨時(shí)性?xún)?nèi)存泄漏
Message發(fā)出之后存儲(chǔ)在MessageQueue中尘分,在Message中存在一個(gè)target,它是Handler的一個(gè)引用丸氛,Message在Queue中存在的時(shí)間過(guò)長(zhǎng)培愁,就會(huì)導(dǎo)致Handler無(wú)法被回收。如果Handler是非靜態(tài)的缓窜,則會(huì)導(dǎo)致Activity或者Service不會(huì)被回收定续,并且消息隊(duì)列是在一個(gè)Looper線(xiàn)程中不斷地輪詢(xún)處理消息,當(dāng)這個(gè)Activity退出時(shí)禾锤,消息隊(duì)列中還有未處理的消息或者正在處理的消息私股,并且消息隊(duì)列中的Message持有Handler實(shí)例的引用,Handler又持有Activity的引用恩掷,所以導(dǎo)致該Activity的內(nèi)存資源無(wú)法及時(shí)回收倡鲸,引發(fā)內(nèi)存泄漏,解決方案如下所示:
1.使用一個(gè)靜態(tài)Handler內(nèi)部類(lèi)黄娘,然后對(duì)Handler持有的對(duì)象(一般是Activity)使用弱引用峭状,這樣在回收時(shí),也可以回收Handler持有的對(duì)象逼争。
2.在Activity的Destory或者Stop時(shí)优床,應(yīng)該移除消息隊(duì)列中的消息,避免Looper線(xiàn)程的消息隊(duì)列中有待處理的消息需要處理誓焦。
7胆敞、容器中的對(duì)象沒(méi)清理造成的內(nèi)存泄漏
在退出程序之前,將集合里的東西clear,然后置為null移层,再退出程序
8仍翰、WebView
WebView都存在內(nèi)存泄漏的問(wèn)題,在應(yīng)用中只要使用一次WebView幽钢,內(nèi)存就不會(huì)被釋放掉歉备,我們可以為WebView開(kāi)啟一個(gè)獨(dú)立的進(jìn)程,使用AIDL與應(yīng)用的主進(jìn)程進(jìn)行通信匪燕,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷(xiāo)毀蕾羊,達(dá)到正常釋放內(nèi)存的目的。
9帽驯、使用ListView時(shí)造成的內(nèi)存泄漏
在構(gòu)造Adapter時(shí)龟再,使用緩存的convertView
另外可參考android官網(wǎng)管理應(yīng)用內(nèi)存的部分https://developer.android.com/topic/performance/memory?hl=zh_cn