Java內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因與解決方案詳解
簡(jiǎn)介
說明
本文介紹Java內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因以及其解決方案。
為什么內(nèi)部類持有外部類會(huì)導(dǎo)致內(nèi)存泄露?
非靜態(tài)內(nèi)部類會(huì)持有外部類,如果有地方引用了這個(gè)非靜態(tài)內(nèi)部類,會(huì)導(dǎo)致外部類也被引用,垃圾回收時(shí)無法回收這個(gè)外部類(即使外部類已經(jīng)沒有其他地方在使用了)。
解決方案
1.不要讓其他的地方持有這個(gè)非靜態(tài)內(nèi)部類的引用,直接在這個(gè)非靜態(tài)內(nèi)部類執(zhí)行業(yè)務(wù)。
2.將非靜態(tài)內(nèi)部類改為靜態(tài)內(nèi)部類。
內(nèi)部類改為靜態(tài)的之后,它所引用的對(duì)象或?qū)傩砸脖仨毷庆o態(tài)的,所以靜態(tài)內(nèi)部類無法獲得外部對(duì)象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到static類型的引用。
相關(guān)網(wǎng)址
匿名內(nèi)部類的內(nèi)存泄露:Java的匿名內(nèi)部類導(dǎo)致內(nèi)存泄露--原因/解決方案
為什么要持有外部類
Java 語(yǔ)言中,非靜態(tài)內(nèi)部類的主要作用有兩個(gè):
當(dāng)內(nèi)部類只在外部類中使用時(shí),匿名內(nèi)部類可以讓外部不知道它的存在,從而減少了代碼的維護(hù)工作。
當(dāng)內(nèi)部類持有外部類時(shí),它就可以直接使用外部類中的變量了,這樣可以很方便的完成調(diào)用,如下代碼所示:
package org.example.a; class Outer{ private String outerName = "Tony"; class Inner{ private String name; public Inner() { this.name = outerName; } } Inner createInner() { return new Inner(); } } public class Demo { public static void main(String[] args) { Outer.Inner inner = new Outer().createInner(); System.out.println(inner); } }
但是,靜態(tài)內(nèi)部類就無法持有外部類和其非靜態(tài)字段了。比如下邊這樣就會(huì)報(bào)錯(cuò)
package org.example.a; class Outer{ private String outerName = "Tony"; static class Inner{ private String name; public Inner() { this.name = outerName; } } Inner createInner() { return new Inner(); } } public class Demo { public static void main(String[] args) { Outer.Inner inner = new Outer().createInner(); System.out.println(inner); } }
報(bào)錯(cuò):
實(shí)例:持有外部類
代碼
package org.example.a; class Outer{ class Inner { } Inner createInner() { return new Inner(); } } public class Demo { public static void main(String[] args) { Outer.Inner inner = new Outer().createInner(); System.out.println(inner); } }
斷點(diǎn)調(diào)試
可以看到:內(nèi)部類持有外部類的對(duì)象的引用,是以“this$0”這個(gè)字段來保存的。
實(shí)例:不持有外部類
代碼
package org.example.a; class Outer{ static class Inner { } Inner createInner() { return new Inner(); } } public class Demo { public static void main(String[] args) { Outer.Inner inner = new Outer().createInner(); System.out.println(inner); } }
斷點(diǎn)調(diào)試
可以發(fā)現(xiàn):內(nèi)部類不再持有外部類了。
實(shí)例:內(nèi)存泄露
簡(jiǎn)介
若內(nèi)部類持有外部類的引用,對(duì)內(nèi)部類的使用很多時(shí),會(huì)導(dǎo)致外部類數(shù)目很多。此時(shí),就算是外部類的數(shù)據(jù)沒有被用到,外部類的數(shù)據(jù)所占空間也不會(huì)被釋放。
本處在外部類存放大量的數(shù)據(jù)來模擬。
代碼
package org.example.a; import java.util.ArrayList; import java.util.List; class Outer{ private int[] data; public Outer(int size) { this.data = new int[size]; } class Innner{ } Innner createInner() { return new Innner(); } } public class Demo { public static void main(String[] args) { List<Object> list = new ArrayList<>(); int counter = 0; while (true) { list.add(new Outer(100000).createInner()); System.out.println(counter++); } } }
測(cè)試
可以看到:運(yùn)行了八千多次的時(shí)候就內(nèi)存溢出了。
不會(huì)內(nèi)存泄露的方案
簡(jiǎn)介
內(nèi)部類改為靜態(tài)的之后,它所引用的對(duì)象或?qū)傩砸脖仨毷庆o態(tài)的,所以靜態(tài)內(nèi)部類無法獲得外部對(duì)象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到static類型的引用。
代碼
package org.example.a; import java.util.ArrayList; import java.util.List; class Outer{ private int[] data; public Outer(int size) { this.data = new int[size]; } static class Inner { } Inner createInner() { return new Inner(); } } public class Demo { public static void main(String[] args) { List<Object> list = new ArrayList<>(); int counter = 0; while (true) { list.add(new Outer(100000).createInner()); System.out.println(counter++); } } }
測(cè)試
可以發(fā)現(xiàn):循環(huán)了四十多萬次都沒有內(nèi)存溢出。
以上就是Java內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因與解決方案詳解的詳細(xì)內(nèi)容,更多關(guān)于Java內(nèi)存泄露的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java如何多線程批量更新10萬級(jí)的數(shù)據(jù)
在處理大數(shù)據(jù)量的批量更新時(shí),直接使用mybatis的updateBatch可能導(dǎo)致效率低下甚至OOM,通過每次處理5000條數(shù)據(jù)的方式雖然安全但效率低,更優(yōu)的解決方案是使用多線程處理,將數(shù)據(jù)分批并多線程執(zhí)行,有效提高了處理速度并保證了系統(tǒng)穩(wěn)定性2024-10-10Mybatis傳入List實(shí)現(xiàn)批量更新的示例代碼
這篇文章主要介紹了Mybatis傳入List實(shí)現(xiàn)批量更新的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Spring Boot 與 Kotlin 使用Redis數(shù)據(jù)庫(kù)的配置方法
Redis是目前業(yè)界使用最廣泛的內(nèi)存數(shù)據(jù)存儲(chǔ)。下面通過本文給大家介紹Spring Boot 與 Kotlin 使用Redis數(shù)據(jù)庫(kù)的配置方法,感興趣的朋友一起看看吧2018-01-01Java 將文件轉(zhuǎn)為字節(jié)數(shù)組知識(shí)總結(jié)及實(shí)例詳解
這篇文章主要介紹了Java 將文件轉(zhuǎn)為字節(jié)數(shù)組實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2016-12-12Eclipse 出現(xiàn)A configuration with this name already exists問題解決方
這篇文章主要介紹了Eclipse 出現(xiàn)A configuration with this name already exists問題解決方法的相關(guān)資料,需要的朋友可以參考下2016-11-11JavaSwing實(shí)現(xiàn)小型學(xué)生管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了JavaSwing實(shí)現(xiàn)小型學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02springboot zuul實(shí)現(xiàn)網(wǎng)關(guān)的代碼
這篇文章主要介紹了springboot zuul實(shí)現(xiàn)網(wǎng)關(guān)的代碼,在為服務(wù)架構(gòu)體系里,網(wǎng)關(guān)是非常重要的環(huán)節(jié),他實(shí)現(xiàn)了很多功能,具體哪些功能大家跟隨小編一起通過本文學(xué)習(xí)吧2018-10-10使用Java的Lucene搜索工具對(duì)檢索結(jié)果進(jìn)行分組和分頁(yè)
這篇文章主要介紹了使用Java的搜索工具Lucene對(duì)檢索結(jié)果進(jìn)行分組和分頁(yè)的方法,Luence是Java環(huán)境中的一個(gè)全文檢索引擎工具包,需要的朋友可以參考下2016-03-03