一、java內(nèi)存模型
1.程序計數(shù)器pc-----線程私有
占用內(nèi)存很信蟊帷;java的多線程是:搶占式的
java中程序計數(shù)器pc為:虛擬機(jī)字節(jié)碼地址窜骄;
native中程序計數(shù)器為:undefined 也就是null锦募;
java虛擬機(jī)規(guī)范中沒有定義此區(qū)域有oom
2.虛擬機(jī)棧-----線程私有
java虛擬機(jī)規(guī)范中定義了兩種異常
stackOverflow
oom內(nèi)存溢出
包含:局部變量表,操作棧邻遏,方法返回地址糠亩,動態(tài)鏈接,額外附加信息准验;
3.本地方法棧-----線程私有(Native Method Stack)
和虛擬機(jī)棧差不多赎线,一個是Java虛擬機(jī)的,一個是native層的
java虛擬機(jī)規(guī)范中定義了兩種異常
stackOverflow
oom內(nèi)存溢出
備注:在有些jvm的實現(xiàn)中糊饱,將本地方法棧和虛擬機(jī)棧合二為一了(代表:sun公司的hotpost虛擬機(jī))垂寥;
4.java堆-----數(shù)據(jù)共享區(qū)
虛擬機(jī)管理的最大一塊內(nèi)存。GC的主戰(zhàn)場(垃圾堆)另锋,有oom異常滞项;
5.方法區(qū)-----數(shù)據(jù)共享區(qū)
包含:常量,靜態(tài)變量砰蠢,類信息蓖扑,即使編譯后的java代碼唉铜,特殊的class類台舱,運(yùn)行時常量池(字面量:java的常量,包括一些public static final,符號引用:類+接口 字段名 方法名 描述符)
二竞惋、引用類型
強(qiáng)軟弱虛
強(qiáng):Object object= new Object();
軟:有用柜去,但不是必須的對象,在內(nèi)存不足的時候拆宛,會將軟引用回收嗓奢;
弱:非必須對象,gc掃過就會回收浑厚;
虛:幽靈引用股耽,不會對生存造成任何影響;沒有辦法得到引用的對象钳幅,但是在對象在回收的時候物蝙,能夠得到通知;
備注:
1.避免OOM----->使用軟引用
2.內(nèi)存得到及時的釋放或回收敢艰;節(jié)省內(nèi)存----->使用弱引用
Object obj = new Object();
//引用隊列诬乞,當(dāng)保存的對象被gc回收時,可以得到回收的引用對象
ReferenceQueue<Object> objectReferenceQueue = new ReferenceQueue<>();
SoftReference<Object> objectSoftReference = new SoftReference<>(obj,objectReferenceQueue);
//得到保存的對象
Object o = objectSoftReference.get();
System.out.println("soft obj:"+o);
obj = null ;
//執(zhí)行g(shù)c不能立即執(zhí)行回收钠导,只是通知gc進(jìn)行掃描震嫉;
System.gc();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//當(dāng)內(nèi)存不足時,可以使用objectReferenceQueue.poll()得到引用的對象
//內(nèi)存不足時使用牡属,可以得到票堵;如果人為的將obj賦值為null,則得不到
Reference<?> poll = objectReferenceQueue.poll();
System.out.println("soft queue:"+poll);
//soft obj:java.lang.Object@2df08e
//soft queue:null逮栅;不為null代表被回收了
ReferenceQueue不為null换衬,代表被回收了;
三证芭、內(nèi)存泄露
內(nèi)存泄露的根本原因:長生命周期的對象擁有短生命周期對象的引用瞳浦,短生命周期對象無法被回收;也就是該被回收的對象因為引用問題無法被回收废士;
1.Alloc Count :申請內(nèi)存的次數(shù)叫潦,也就是申請的對象數(shù);
2.Shallow Size:對象占用的內(nèi)存大泄傧酢矗蕊;
3.Retained Size:對象引用組占用的大小氢架;
4.轉(zhuǎn)換mat標(biāo)準(zhǔn)文件
轉(zhuǎn)換工具目錄在/SDK/platform-tools/下面
hprof-conv -z src dst :將src轉(zhuǎn)換為dst
例如:hprof-conv -z 1.hprof 1_mat.hprof
5.系統(tǒng)輸入法內(nèi)存泄露bug:
InputMethodManager ----內(nèi)部類持有->mCurRootView---->也就是系統(tǒng)的DecorView---->這個DecorView持有mContext上下文對象傻咖;
解決方法:
@Override
protected void onDestroy() {
super.onDestroy();
//去除系統(tǒng)級別的輸入法造成的內(nèi)存泄露
/**
* InputMethodManager ----內(nèi)部類持有->mCurRootView---->也就是系統(tǒng)的DecorView---->這個DecorView持有mContext上下文對象;
*
* 這里可以把InputMethodManager看作是GCRoot岖研,只有打斷這條鏈卿操,才能釋放mCurRootView持有的上下文對象警检;
* 因為無法直接獲取InputMethodMananger中的mCurRootView對象,只有使用反射來獲取
*/
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
Class<? extends InputMethodManager> aClass = inputMethodManager.getClass();
try {
//getDeclaredField() 獲取全部的屬性(不包含繼承所得屬性) getField()只能獲取public修飾的屬性(包含繼承所得)
Field mCurRootViewField = aClass.getDeclaredField("mCurRootView");
mCurRootViewField.setAccessible(true);
Object mCurRootView = mCurRootViewField.get(inputMethodManager);
if (null != mCurRootView) {
Context context = ((View) mCurRootView).getContext();
if (context == this) {
//破壞gc引用鏈
mCurRootViewField.set(inputMethodManager,null);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
5.檢查內(nèi)存泄露 除了使用mat 還有使用LeakCanary工具害淤;
6.如何防止單例模式內(nèi)存泄露扇雕?
如果必須傳入Activity的上下文對象,可以使用弱引用來持有窥摄,便于及時被gc回收镶奉;
如果非必須 可以使用application的上下文對象;
7.IO操作如何防止內(nèi)存泄露崭放?
在IO操作中哨苛,必須要在finally中進(jìn)行資源的關(guān)閉和釋放;
8.Handler或Thread引起的內(nèi)存泄露币砂?
主要由于使用內(nèi)部類的原因(非靜態(tài)內(nèi)部類持有外部類的引用)移国;
解決方式:使用靜態(tài)內(nèi)部類+弱引用方式;
創(chuàng)建一個靜態(tài)Handler內(nèi)部類道伟,然后對Handler持有的對象使用弱引用迹缀,這樣在回收時也可以回收Handler持有的對象,這樣雖然避免了Activity泄漏蜜徽,不過Looper線程的消息隊列中還是可能會有待處理的消息祝懂,所以我們在Activity的Destroy時或者Stop時應(yīng)該移除消息隊列中的消息,
使用mHandler.removeCallbacksAndMessages(null);是移除消息隊列中所有消息和所有的Runnable拘鞋。當(dāng)然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();來移除指定的Runnable和Message砚蓬。
Thread的內(nèi)存泄露和Handler一樣,處理方式也是一樣的盆色;Thread內(nèi)存泄露的主要原因是因為線程生命周期的不可控灰蛙。比如線程是 Activity 的內(nèi)部類,則線程對象中保存了 Activity 的一個引用隔躲,當(dāng)線程的 run 函數(shù)耗時較長沒有結(jié)束時摩梧,線程對象是不會被銷毀的,因此它所引用的老的 Activity 也不會被銷毀宣旱,因此就出現(xiàn)了內(nèi)存泄露的問題仅父。
9.集合類產(chǎn)生的內(nèi)存泄露?
集合類如果僅僅有添加元素的方法浑吟,而沒有相應(yīng)的刪除機(jī)制笙纤,導(dǎo)致內(nèi)存被占用。如果這個集合類是全局性的變量 (比如類中的靜態(tài)屬性组力,全局性的 map 等即有靜態(tài)引用或 final 一直指向它)省容,那么沒有相應(yīng)的刪除機(jī)制,很可能導(dǎo)致集合所占用的內(nèi)存只增不減燎字。
10.靜態(tài)成員引起的內(nèi)存泄露腥椒?
Static成員作為gc root阿宅,如果一個對象被static聲明,這個對象會一直存活直到程序進(jìn)程停止寞酿。