亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

簡(jiǎn)單介紹內(nèi)存泄漏&內(nèi)存抖動(dòng)

內(nèi)存泄漏:

Memory leak, 是一種資源泄漏,主因是計(jì)算機(jī)程序?qū)Υ鎯?chǔ)器配置管理失當(dāng),失去對(duì)一段已分配內(nèi)存空間的控制,造成程序繼續(xù)占用已經(jīng)不再使用的內(nèi)存空間,或是存儲(chǔ)器所存儲(chǔ)之對(duì)象無(wú)法透過(guò)執(zhí)行代碼而訪問(wèn),令內(nèi)存資源空耗。

簡(jiǎn)單來(lái)說(shuō),內(nèi)存泄漏是指無(wú)法正確回收已經(jīng)不再使用的內(nèi)存。

 

舉例:

請(qǐng)注意以下的例子是虛構(gòu)的

在此例中的應(yīng)用程序是一個(gè)簡(jiǎn)單軟件的一小部分,用來(lái)控制電梯的運(yùn)作。<br>此部分軟件當(dāng)乘客在電梯內(nèi)按下一樓層的按鈕時(shí)運(yùn)行。

當(dāng)按下按鈕時(shí):

要求使用存儲(chǔ)器,用作記住目的樓層

 

把目的樓層的數(shù)字儲(chǔ)存到存儲(chǔ)器中

電梯是否已到達(dá)目的樓層?

如是,沒(méi)有任何事需要做:程序完成

否則:

等待直至電梯停止

到達(dá)指定樓層

釋放剛才用作記住目的樓層的存儲(chǔ)器

 

此程序有一處會(huì)造成存儲(chǔ)器泄漏:如果在電梯所在樓層按下該層的按鈕(即上述程序的第4步),程序?qū)⒂|發(fā)判斷條件而結(jié)束運(yùn)行,但存儲(chǔ)器仍一直被占用而沒(méi)有被釋放。這種情況發(fā)生得越多,泄漏的存儲(chǔ)器也越多。

 

這個(gè)小錯(cuò)誤不會(huì)造成即時(shí)影響。因?yàn)槿瞬粫?huì)經(jīng)常在電梯所在樓層按下同一層的按鈕。而且在通常情況下,電梯應(yīng)有足夠的存儲(chǔ)器以應(yīng)付上百次、上千次類似的情況。不過(guò),電梯最后仍有可能消耗完所有存儲(chǔ)器。這可能需要數(shù)個(gè)月或是數(shù)年,所以在簡(jiǎn)單的測(cè)試下這個(gè)問(wèn)題不會(huì)被發(fā)現(xiàn)。

 

而這個(gè)例子導(dǎo)致的后果會(huì)是不那么令人愉快。至少,電梯不會(huì)再理會(huì)前往其他樓層的要求。更嚴(yán)重的是,如果程序需要存儲(chǔ)器去開(kāi)啟電梯門,那可能有人被困電梯內(nèi),因?yàn)殡娞輿](méi)有足夠的存儲(chǔ)器去開(kāi)啟電梯門。

 

存儲(chǔ)器泄漏只會(huì)在程序運(yùn)行的時(shí)間內(nèi)持續(xù)。例如:關(guān)閉電梯的電源時(shí),程序終止運(yùn)行。當(dāng)電源再度開(kāi)啟,程序會(huì)再次運(yùn)行而存儲(chǔ)器會(huì)重置,而這種緩慢的泄漏則會(huì)從頭開(kāi)始再次發(fā)生。

 

內(nèi)存抖動(dòng)

源自Android文檔中的Memory churn一詞,中文翻譯為內(nèi)存抖動(dòng)。

指快速頻繁的創(chuàng)建對(duì)象從而產(chǎn)生的性能問(wèn)題。

引用Android文檔原文:

垃圾回收事件通常不會(huì)影響應(yīng)用的性能。不過(guò),如果在短時(shí)間內(nèi)發(fā)生許多垃圾回收事件,就可能會(huì)快速耗盡幀時(shí)間。系統(tǒng)花在垃圾回收上的時(shí)間越多,能夠花在呈現(xiàn)或流式傳輸音頻等其他任務(wù)上的時(shí)間就越少。

 

通常,“內(nèi)存抖動(dòng)”可能會(huì)導(dǎo)致出現(xiàn)大量的垃圾回收事件。實(shí)際上,內(nèi)存抖動(dòng)可以說(shuō)明在給定時(shí)間內(nèi)出現(xiàn)的已分配臨時(shí)對(duì)象的數(shù)量。

 

例如,您可以在 `for` 循環(huán)中分配多個(gè)臨時(shí)對(duì)象。或者,您也可以在視圖的 `onDraw()` 函數(shù)中創(chuàng)建新的 `Paint` 或 `Bitmap` 對(duì)象。在這兩種情況下,應(yīng)用都會(huì)快速創(chuàng)建大量對(duì)象。這些操作可以快速消耗新生代 (young generation) 區(qū)域中的所有可用內(nèi)存,從而迫使垃圾回收事件發(fā)生。

 

內(nèi)存泄漏(Memory leak)的產(chǎn)生和避免方式

JAVA內(nèi)存泄漏的根本原因是長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用就很可能發(fā)生內(nèi)存泄漏。

盡管短生命周期對(duì)象已經(jīng)不再需要,但因?yàn)殚L(zhǎng)生命周期依舊持有它的引用,故不能被回收而導(dǎo)致內(nèi)存泄漏。

 

幾種引起內(nèi)存泄漏的問(wèn)題:

靜態(tài)集合類引起的內(nèi)存泄漏

HashMap、ArrayList等集合以靜態(tài)形式聲明時(shí),這些靜態(tài)對(duì)象的生命周期與應(yīng)用程序一致。他們所引用的對(duì)象也無(wú)法被釋放,因?yàn)樗鼈円脖患弦弥?/p>

private static HashMap<String, Object> a = new HashMap();
public static void main(String args[]) {
for (int i = 0; i < 1000; i++) {
Object tO = new Object();
a.put("0", tO);
tO = null;
}
}

 

如果僅僅釋放引用本身(tO = null),ArrayList依然在引用該對(duì)象,GC無(wú)法回收。

 

監(jiān)聽(tīng)器

在Java應(yīng)用中,通常會(huì)用到很多監(jiān)聽(tīng)器,一般通過(guò)addXXXXListener()實(shí)現(xiàn)。但釋放對(duì)象時(shí)通常會(huì)忘記刪除監(jiān)聽(tīng)器,從而增加內(nèi)存泄漏的風(fēng)險(xiǎn)。

 

各種連接

如數(shù)據(jù)庫(kù)連接、網(wǎng)絡(luò)連接(Socket)和I/O連接。忘記顯式調(diào)用close()方法引起的內(nèi)存泄漏。

 

內(nèi)部類和外部模塊的引用

內(nèi)部類的引用是很容易被遺忘的一種,一旦沒(méi)有釋放可能會(huì)導(dǎo)致一系列后續(xù)對(duì)象無(wú)法釋放。此外還要小心外部模塊不經(jīng)意的引用,內(nèi)部類是否提供相應(yīng)的操作去除外部引用。

 

單例模式

由于單例的靜態(tài)特性,使其生命周期與應(yīng)用的生命周期一樣長(zhǎng),一旦使用不恰當(dāng)極易造成內(nèi)存泄漏。如果單利持有外部引用,需要注意提供釋放方式,否則當(dāng)外部對(duì)象無(wú)法被正常回收時(shí),會(huì)進(jìn)而導(dǎo)致內(nèi)存泄漏。

 

常見(jiàn)的內(nèi)存泄漏處理方式:

集合類泄漏

如集合的使用范圍超過(guò)邏輯代碼的范圍,需要格外注意刪除機(jī)制是否完善可靠。比如由靜態(tài)屬性static指向的集合。

 

單利泄漏

以下為簡(jiǎn)單邏輯代碼,只為舉例說(shuō)明內(nèi)存泄漏問(wèn)題,不保證單利模式的可靠性。

public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance == null) {
instance = new AppManager(context);
}
return instance;
}
}

 

AppManager創(chuàng)建時(shí)需要傳入一個(gè)Context,這個(gè)Context的生命周期長(zhǎng)短至關(guān)重要。

1. 如果傳入的是Application的Context,因?yàn)锳pplication的生命周期等同于應(yīng)用的生命周期,所以沒(méi)有任何問(wèn)題。

2. 如果傳入的是Activity的Context,則需要考慮這個(gè)Activity是否在整個(gè)生命周期都不會(huì)被回收了,如果不是,則會(huì)造成內(nèi)存泄漏。

 

非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實(shí)例造成的內(nèi)存泄漏

public class MyActivity extends AppCompatActivity {
private static MyInnerClass mInnerClass = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (mInnerClass == null) {
mInnerClass = new MyInnerClass();
}
}
class MyInnerClass {
...
}
}

 

內(nèi)部類持有外部類引用,而static聲明的對(duì)象聲明周期通常會(huì)比Activity長(zhǎng)。即使關(guān)閉這個(gè)頁(yè)面,由于mInnerClass為靜態(tài)的,并且持有MyActivity的引用,導(dǎo)致無(wú)法回收此頁(yè)面從而引起內(nèi)存泄漏。

應(yīng)該將該內(nèi)部類單獨(dú)封裝為一個(gè)單例來(lái)使用。

 

匿名內(nèi)部類/異步線程

public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
new Thread(new Runnable() {
@Override
public void run() {
...
}
}).start();
}
}

 

Runnable都使用了匿名內(nèi)部類,將持有MyActivity的引用。如果任務(wù)在Activity銷毀前未完成,將導(dǎo)致Activity的內(nèi)存無(wú)法被回收,從而造成內(nèi)存泄漏。

解決方法:將Runnable獨(dú)立出來(lái)或使用靜態(tài)內(nèi)部類,可以避免因持有外部對(duì)象導(dǎo)致的內(nèi)存泄漏。

 

Handler造成的內(nèi)存泄漏

public class SampleActivity extends AppCompatActivity {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
...
}
}, 300000);
finish();
}
}

 

Handler屬于TLS(Thread Local Storage)變量,生命周期與Activity是不一致的,容易導(dǎo)致持有的對(duì)象無(wú)法正確被釋放

當(dāng)Android應(yīng)用程序啟動(dòng)時(shí),該應(yīng)用程序的主線程會(huì)自動(dòng)創(chuàng)建一個(gè)Looper對(duì)象和與之關(guān)聯(lián)的MessageQueue。

當(dāng)主線程中實(shí)例化一個(gè)Handler對(duì)象后,它就會(huì)自動(dòng)與主線程Looper的MessageQueue關(guān)聯(lián)起來(lái)。所有發(fā)送到MessageQueue的Messag都會(huì)持有Handler的引用,所以Looper會(huì)據(jù)此回調(diào)Handle的handleMessage()方法來(lái)處理消息。只要MessageQueue中有未處理的Message,Looper就會(huì)不斷的從中取出并交給Handler處理。

另外,主線程的Looper對(duì)象會(huì)伴隨該應(yīng)用程序的整個(gè)生命周期。

在Java中,非靜態(tài)內(nèi)部類和匿名類內(nèi)部類都會(huì)潛在持有它們所屬的外部類的引用,但是靜態(tài)內(nèi)部類卻不會(huì)。

當(dāng)該 Activity 被finish()掉時(shí),延遲執(zhí)行任務(wù)的 Message 還會(huì)繼續(xù)存在于主線程中,它持有該 Activity 的 Handler 引用,所以此時(shí)finish()掉的 Activity 就不會(huì)被回收了從而造成內(nèi)存泄漏(因 Handler 為非靜態(tài)內(nèi)部類,它會(huì)持有外部類的引用,在這里就是指 SampleActivity)。

解決方法:在 Activity 中避免使用非靜態(tài)內(nèi)部類,比如上面我們將 Handler 聲明為靜態(tài)的,則其存活期跟 Activity 的生命周期就無(wú)關(guān)了。同時(shí)通過(guò)弱引用的方式引入 Activity,避免直接將 Activity 作為 context 傳進(jìn)去,見(jiàn)如下代碼:

public class SampleActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mactivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
...
}
}
}
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable mRunnable = new Runnable() {
@Override
public void run() { ... }
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
mHandler.postDelayed(mRunnable, 300000);
finish();
}
}

 

避免不必要的靜態(tài)成員變量

對(duì)于BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等資源的使用,應(yīng)在Activity銷毀前及時(shí)關(guān)閉或注銷。

不使用WebView對(duì)象時(shí),應(yīng)調(diào)用`destroy()`方法銷毀。

Android技術(shù)分享|Android 中部分內(nèi)存泄漏示例及解決方案

 

分享到:
標(biāo)簽:Android
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定