日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python 单例模式内存泄露_彻底搞懂Java内存泄露

發布時間:2025/3/15 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python 单例模式内存泄露_彻底搞懂Java内存泄露 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

之前一直在簡書寫作,第一次發布到SF上來,也是第一次使用SF,后面會盡量同步到SF,更多文章請關注:

簡書?編程之樂

轉載請注明出處:謝謝!

Java內存回收方式

Java判斷對象是否可以回收使用的而是可達性分析算法。

在主流的商用程序語言中(Java和C#),都是使用可達性分析算法判斷對象是否存活的。這個算法的基本思路就是通過一系列名為"GC Roots"的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,下圖對象object5, object6, object7雖然有互相判斷,但它們到GC Roots是不可達的,所以它們將會判定為是可回收對象。

在Java語言里,可作為GC Roots對象的包括如下幾種:

a.虛擬機棧(棧楨中的本地變量表)中的引用的對象

b.方法區中的類靜態屬性引用的對象

c.方法區中的常量引用的對象

d.本地方法棧中JNI的引用的對象

摘自《深入理解Java虛擬機》

使用leakcanary檢測泄漏

關于LeakCanary使用參考以下文章:

LeakCanary的內存泄露提示一般會包含三個部分:

第一部分(LeakSingle類的sInstance變量)

引用第二部分(LeakSingle類的mContext變量),

導致第三部分(MainActivity類的實例instance)泄露.

leakcanary使用注意

即使是空的Activity,如果檢測泄露時候遇到了如下這樣的泄露,注意,把refWatcher.watct()放在onDestroy里面即可解決,或者忽略這樣的提示。

由于文章已寫很多,下面的就不再修改,忽略這種錯誤即可。

* com.less.demo.TestActivity has leaked:

* GC ROOT static android.app.ActivityThread.sCurrentActivityThread

* references android.app.ActivityThread.mActivities

* references android.util.ArrayMap.mArray

* references array java.lang.Object[].[1]

* references android.app.ActivityThread$ActivityClientRecord.activity

* leaks com.less.demo.TestActivity instance

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

leakcanary和代碼示例說明內存泄露

案例一(靜態成員引起的內存泄露)

public class App extends Application {

private RefWatcher refWatcher;

@Override

public void onCreate() {

super.onCreate();

refWatcher = LeakCanary.install(this);

}

public static RefWatcher getRefWatcher(Context context) {

App application = (App) context.getApplicationContext();

return application.refWatcher;

}

}

測試內部類持有外部類引用,內部類是靜態的(GC-ROOT,將一直連著這個外部類實例),靜態的會和Application一個生命周期,這會導致一直持有外部類引用(內部類隱含了一個成員變量$0), 即使外部類制空= null,也無法釋放。

OutterClass

public class OutterClass {

private String name;

class Inner{

public void list(){

System.out.println("outter name is " + name);

}

}

}

TestActivity

public class TestActivity extends Activity {

// 靜態的內部類實例

private static OutterClass.Inner innerClass;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

OutterClass outterClass = new OutterClass();

innerClass = outterClass.new Inner();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(outterClass);// 監控的對象

outterClass = null;

}

案例二(單例模式引起的內存泄露)

DownloadManager

public class DownloadManager {

private static DownloadManager instance;

private Task task ;

public static DownloadManager getInstance(){

if (instance == null) {

instance = new DownloadManager();

}

return instance;

}

public Task newTask(){

this.task = new Task();

return task;

}

}

Task

public class Task {

private Call call;

public Call newCall(){

this.call = new Call();

return call;

}

}

Call

public class Call {

public void execute(){

System.out.println("=========> execute call");

}

}

TestActivity

public class TestActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

RefWatcher refWatcher = App.getRefWatcher(this);

Task task = DownloadManager.getInstance().newTask();

Call call = task.newCall();

call.execute();

refWatcher.watch(call);// 監控的對象

call = null; // 無法回收,DownloadManager是靜態單例,引用task,task引用了call,即使call置為空,也無法回收,切斷GC_ROOT 聯系即可避免內存泄露,即置task為空。

}

}

部分日志打印如下:當前的GC_ROOT是DownloadManager的instance實例。

In com.leakcanary.demo:1.0:1.

* com.less.demo.Call has leaked:

* GC ROOT static com.less.demo.DownloadManager.instance

* references com.less.demo.DownloadManager.task

* references com.less.demo.Task.call

* leaks com.less.demo.Call instance

關于上面兩種方式導致的內存泄露問題,這里再舉兩個案例說明以加強理解。

案例三(靜態變量導致的內存泄露)

public class TestActivity extends Activity {

private static Context sContext;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

sContext = this;

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);// 監控的對象

}

打印日志如下:

In com.leakcanary.demo:1.0:1.

com.less.demo.TestActivity has leaked:

GC ROOT static com.less.demo.TestActivity.sContext

leaks com.less.demo.TestActivity instance

從這段日志可以分析出:聲明static后,sContext的生命周期將和Application一樣長,Activity即使退出到桌面,Application依然存在->sContext依然存在,GC此時想回收Activity卻發現Activity仍然被sContext(GC-ROOT連接著),導致死活回收不了,內存泄露。

上面的代碼改造一下,如下。

public class TestActivity extends Activity {

private static View sView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

sView = new View(this);

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

日志如下

In com.leakcanary.demo:1.0:1.

com.less.demo.TestActivity has leaked:

GC ROOT static com.less.demo.TestActivity.sView

references android.view.View.mContext

leaks com.less.demo.TestActivity instance

案例四(單例模式導致的內存泄露)

DownloadManager

public class DownloadManager {

private static DownloadManager instance;

private List mListeners = new ArrayList<>();

public interface DownloadListener {

void done();

}

public static DownloadManager getInstance(){

if (instance == null) {

instance = new DownloadManager();

}

return instance;

}

public void register(DownloadListener downloadListener){

if (!mListeners.contains(downloadListener)) {

mListeners.add(downloadListener);

}

}

public void unregister(DownloadListener downloadListener){

if (mListeners.contains(downloadListener)) {

mListeners.remove(downloadListener);

}

}

}

TestActivity

public class TestActivity extends Activity implements DownloadManager.DownloadListener{

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

DownloadManager.getInstance().register(this);

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

@Override

protected void onDestroy() {

super.onDestroy();

// 忘記 unregister

// DownloadManager.getInstance().unregister(this);

}

@Override

public void done() {

System.out.println("done!");

}

}

In com.leakcanary.demo:1.0:1.

* com.less.demo.TestActivity has leaked:

* GC ROOT static com.less.demo.DownloadManager.instance

* references com.less.demo.DownloadManager.mListeners

* references java.util.ArrayList.array

* references array java.lang.Object[].[0]

* leaks com.less.demo.TestActivity instance

錯誤寫法一定導致內存泄露嗎?

答案是:否定的。

如下案例,有限時間內是可以挽救內存泄露發生的,所以控制好生命周期,其他情況:如單例對象(靜態變量)的生命周期比其持有的sContext

的生命周期更長時 ->內存泄露,更短時->躲過內存泄露。

public class TestActivity extends Activity {

private static Context sContext;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

sContext = this;

new Handler().postDelayed(new Runnable() {

@Override

public void run() {

sContext = null;

}

},1000);// 分別測試1s和12s,前者不會內存泄露,后者一定泄露。所以如果趕在GC之前切斷GC_ROOT是可以避免內存泄露的。

}

@Override

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

Handler 引起的內存泄露詳細分析

handler導致的內存泄露可能我們大多數都犯過.

注意以下代碼中的注釋,非靜態內部類雖然持有外部類引用,但是持有并不代表一定泄露,而是看gc時誰的命長。經過測試 情況(1)始終沒有內存泄露。

為什么會這樣, 很早閱讀Handler源碼時候記得這幾個貨都是互相引用來引用去的,Message有個target字段, message.target = handler;

handler.post(message);又把這個message推入了MessageQueue中,而MessageQueue是在一個Looper線程中不斷輪詢處理消息,而有時候message還是個老不死,能夠重復利用。如果當Activity退出時候,還有消息未處理或正在處理,由于message引用handler,handler又引用Activity,此時將引發內存泄露。

public class TestActivity extends Activity {

private Handler handler = new Handler() {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

System.out.println("===== handle message ====");

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

// (1) 不會導致內存泄露

handler.sendEmptyMessageDelayed(0x123,0);

// (2) 會導致內存泄露,非靜態內部類(包括匿名內部類,比如這個 Handler 匿名內部類)會引用外部類對象 this(比如 Activity)

// 當它使用了 postDelayed 的時候,如果 Activity 已經 finish 了,而這個 handler 仍然引用著這個 Activity 就會致使內存泄漏

// 因為這個 handler 會在一段時間內繼續被 main Looper 持有,導致引用仍然存在.

handler.sendEmptyMessageDelayed(0x123, 12000);

}

@Override

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

com.less.demo.TestActivity has leaked:

* GC ROOT android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mH

* references com.android.internal.view.IInputConnectionWrapper$MyHandler.mQueue

* references android.os.MessageQueue.mMessages

* references android.os.Message.target , matching exclusion field android.os.Message#target

* references com.less.demo.TestActivity$1.this$0 (anonymous subclass of android.os.Handler)

* leaks com.less.demo.TestActivity instance

知道了原理后,即使寫出易于內存泄露的代碼也能夠避免內存泄露啦。

上述代碼只需在onDestroy()函數中調用mHandler.removeCallbacksAndMessages(null);

在Activity退出的時候的移除消息隊列中所有消息和所有的Runnable。

內部類引起的內存泄露

內部類種類大致如下:

非靜態內部類(成員內部類)

靜態內部類(嵌套內部類)

局部內部類(定義在方法內或者作用域內的類,好似局部變量,所以不能有訪問控制符和static等修飾)

匿名內部類(沒有名字,僅使用一次new個對象即扔掉類的定義)

為什么非靜態內部類持有外部類引用,靜態內部類不持有外部引用。

這個問題非常簡單,就像 static的方法只能調用static的東西,非static可以調用非static和static的一樣。static--> 針對class, 非static->針對 對象,我是這么簡單理解的。看圖:

匿名內部類

將局部內部類的使用再深入一步,假如只創建某個局部內部類的一個對象,就不必命名了。

匿名內部類的類型可以是如下幾種方式。

接口匿名內部類

抽象類匿名內部類

類匿名內部類

匿名內部類總結:

其實主要就是類定義一次就失效了,主要使用的是這個類(不知道名字)的實例。根據內部類的特性,能夠實現回調和閉包。

JavaScript和Python的回調傳遞的是fuction,Java傳遞的是object。

Java中常常用到回調,而回調的本質就是傳遞一個對象,JavaScript或其他語言則是傳遞一個函數(如Python,或者C,使用函數指針的方式),由于傳遞一個對象可以攜帶其他的一些信息,所以Java認為傳遞一個對象比傳遞一個函數要靈活的多(當然java也可以用Method反射對象傳遞函數)。參考《Java核心技術》

非靜態內部類導致內存泄露(前提dog的命長)

下面的案例將導致內存泄露

其中private static Dog dog ; 導致Dog的命比TestActivity長,這就糟糕了,但是注意,如果改為private Dog dog ; 即使Dog是非靜態內部類,也不會導致內存泄露,要死也是Dog先死,畢竟Dog是TestActivity的家庭成員,開掛也得看主人。

public class TestActivity extends Activity {

private static Dog dog ;

class Dog {

public void say(){

System.out.println("I am lovely dog!");

}

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

dog = new Dog();

}

@Override

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

In com.leakcanary.demo:1.0:1.

* com.less.demo.TestActivity has leaked:

* GC ROOT static com.less.demo.TestActivity.dog

* references com.less.demo.TestActivity$Dog.this$0

* leaks com.less.demo.TestActivity instance

哪些內部類或者回調函數是否持有外部類對象?

一個反射案例說明一切

/**

* 作者: limitless

* 描述: 一個案例測試所有類型內部類對外部類對象的持有情況

* 網站: https://github.com/wangli0

*/

public class Main {

/* 持有外部類引用 */

private IAppListener mAppListener = new IAppListener() {

private String name;

@Override

public void done() {

System.out.println("匿名內部類對象作為成員變量");

}

};

/* 未持有 */

private static IAppListener sAppListener = new IAppListener() {

private String name;

@Override

public void done() {

System.out.println("匿名內部類對象作為static成員變量");

}

};

public static void main(String args[]) {

Main main = new Main();

main.test1();

main.test2();

main.test3();// test3 《=》test4

main.test4();

main.test5();

main.test6();

}

class Dog {

private String name;

}

/* 持有外部類引用 */

public void test1(){

Dog dog = new Dog();

getAllFieldName(dog.getClass());

}

static class Cat {

private String name;

}

/* 未持有 */

private void test2() {

Cat cat = new Cat();

getAllFieldName(cat.getClass());

}

/* 持有外部類引用 */

private void test3() {

class Monkey{

String name;

}

Monkey monkey = new Monkey();

getAllFieldName(monkey.getClass());

}

/* 持有外部類引用 */

private void test4() {

// 常用作事件回調的地方(有可能引起內存泄露)

IAppListener iAppListener = new IAppListener() {

private String name;

@Override

public void done() {

System.out.println("匿名內部類");

}

};

getAllFieldName(iAppListener.getClass());

}

/* 持有外部類引用 */

private void test5() {

getAllFieldName(mAppListener.getClass());

}

/* 未持有 */

private void test6() {

getAllFieldName(sAppListener.getClass());

}

private void getAllFieldName(Class> clazz) {

System.out.println("className: ======> " + clazz.getSimpleName());

Field[] fields = clazz.getDeclaredFields();

for (Field field : fields) {

System.out.println(field.getName());

}

}

}

上述結果足夠說明,即使是方法內的回調對象也是持有外部類引用的,那么雖然作用域是局部的,也有存在內存泄露的可能。我多次強調 持有某對象 不代表一定泄露,看的是誰命長。回調在Android開發過程中幾乎處處可見,如果持有就會內存泄露的話,那幾乎就沒法玩了。

一般情況下,我們常常設置某個方法內的xx.execute(new Listener(){xx});是不會導致內存泄露的,這個方法執行完,局部作用域就失效了。但是如果在execute(listener);過程中,某個單例模式的對象 突然引用了這個listener對象,那么就會導致內存泄露。

下面用實例證明我的想法

TestActivity

public class TestActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

Task task = new Task();

task.execute(new ICallback() {

@Override

public void done() {

System.out.println("下載完成!");

}

});

}

@Override

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

Task

public class Task {

public void execute(ICallback iCallback) {

DownloadManager.getInstance().execute(iCallback);

}

}

DownloadManager

public class DownloadManager {

public static DownloadManager instance;

private ICallback mICallback;

public static DownloadManager getInstance(){

if (instance == null) {

instance = new DownloadManager();

}

return instance;

}

public void execute(ICallback iCallback) {

this.mICallback = iCallback;// 反例,千萬不要這么做,將導致內存泄露,如果注釋掉這行,內存泄露不會發生

iCallback.done();

}

這足以證明我的想法是正確的,Callback的不巧當使用同樣會導致內存泄露 的發送。

總結

如果某些單例需要使用到Context對象,推薦使用Application的context,不要使用Activity的context,否則容易導致內存泄露。單例對象的生命周期和Application一致,這樣Application和單例對象就一起銷毀。

優先使用靜態內部類而不是非靜態的,因為非靜態內部類持有外部類引用可能導致垃圾回收失敗。如果你的靜態內部類需要宿主Activity的引用來執行某些東西,你要將這個引用封裝在一個WeakReference中,避免意外導致Activity泄露,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收 只被弱引用關聯 的對象,只被 說明這個對象本身已經沒有用處了。

public class TestActivity extends Activity {

private MyHandler myHandler = new MyHandler(this);

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

}

static class MyHandler extends Handler {

private WeakReference mWeakReference;

public MyHandler(Activity activity){

mWeakReference = new WeakReference(activity);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

Toast.makeText(mWeakReference.get(), "xxxx", Toast.LENGTH_LONG).show();

Log.d("xx", mWeakReference.get().getPackageName());

}

}

@Override

protected void onDestroy() {

super.onDestroy();

RefWatcher refWatcher = App.getRefWatcher(this);

refWatcher.watch(this);

}

}

總結

以上是生活随笔為你收集整理的python 单例模式内存泄露_彻底搞懂Java内存泄露的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。