Java匿名內(nèi)部類導致內(nèi)存泄露的原因與解決方案詳解
簡介
說明
本文用示例介紹匿名內(nèi)部類會導致內(nèi)存泄漏的原因及其解決方案。
相關(guān)網(wǎng)址
普通內(nèi)部類的內(nèi)存泄露:Java內(nèi)部類持有外部類導致內(nèi)存泄露--原因/解決方案
為什么要持有外部類
Java 語言中,非靜態(tài)內(nèi)部類的主要作用有兩個:
當匿名內(nèi)部類只在外部類(主類)中使用時,匿名內(nèi)部類可以讓外部不知道它的存在,從而減少了代碼的維護工作。
當匿名內(nèi)部類持有外部類時,它就可以直接使用外部類中的變量了,這樣可以很方便的完成調(diào)用,如下代碼所示:
package org.example.a;
import java.util.ArrayList;
import java.util.List;
public class Demo {
private static String name = "Tony";
public static void main(String[] args) {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
add(name);
}};
System.out.println(list);
}
}實例:持有外部類
代碼
package org.example.a;
import java.util.ArrayList;
import java.util.List;
class Test{
public List<String> createList() {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
return list;
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(new Test().createList());
}
}編譯查看class
命令:javac Demo.java
結(jié)果:

Idea查看Test$1.class(可以發(fā)現(xiàn):持有了一個外部類Test對象)
package org.example.a;
import java.util.ArrayList;
class Test$1 extends ArrayList<String> {
Test$1(Test var1) {
this.this$0 = var1;
this.add("a");
this.add("b");
}
}
Idea查看Test.class
package org.example.a;
import java.util.ArrayList;
import java.util.List;
class Test {
Test() {
}
public List<String> createList() {
ArrayList var1 = new ArrayList<String>() {
{
this.add("a");
this.add("b");
}
};
return var1;
}
}Idea查看Demo.class
package org.example.a;
public class Demo {
public Demo() {
}
public static void main(String[] var0) {
System.out.println((new Test()).createList());
}
}
查看字節(jié)碼
命令
javap -c Test$1.class
結(jié)果
Compiled from "Demo.java"
class org.example.a.Test$1 extends java.util.ArrayList<java.lang.String> {
final org.example.a.Test this$0;
org.example.a.Test$1(org.example.a.Test);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:Lorg/example/a/Test;
5: aload_0
6: invokespecial #2 // Method java/util/ArrayList."<init>":()V
9: aload_0
10: ldc #3 // String a
12: invokevirtual #4 // Method add:(Ljava/lang/Object;)Z
15: pop
16: aload_0
17: ldc #5 // String b
19: invokevirtual #4 // Method add:(Ljava/lang/Object;)Z
22: pop
23: return
}
分析
關(guān)鍵代碼的在 putfield 這一行,此行表示有一個對 Test 的引用被存入到 this$0 中,也就是說這個匿名內(nèi)部類持有了外部類的引用。
代碼驗證
package org.example.a;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
class Test{
public List<String> createList() {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
return list;
}
}
public class Demo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
List<String> list = new Test().createList();
// 獲取一個類的所有字段
Field field = list.getClass().getDeclaredField("this$0");
// 設(shè)置允許方法私有的 private 修飾的變量
field.setAccessible(true);
System.out.println(field.get(list).getClass());
}
}打個斷點(注意:我這里是用Object模式(右鍵Variables里的this=> View as=> Object))
可見:它是持有外部類Test的對象的。

執(zhí)行結(jié)果:
class org.example.a.Test
什么時候會內(nèi)存泄露
非靜態(tài)方法返回匿名內(nèi)部類的引用可能導致內(nèi)存泄露,例:
?class Test{
public List<String> createList() {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
return list;
}
}
跟上邊“普通內(nèi)部類” 一樣,若Test類里邊有比較大的對象,而這些大對象根本沒被用到,則會內(nèi)存泄露。
不會內(nèi)存泄漏的方案
方案1:不返回內(nèi)部類對象引用
業(yè)務直接處理,不返回內(nèi)部類對象引用
class Test{
public void createList() {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
System.out.println(list);
}
}
方案2:匿名內(nèi)部類改為靜態(tài)的
將匿名內(nèi)部類改為靜態(tài)的。此時,內(nèi)部類不會持有外部類的對象的引用。
為什么這樣就不會內(nèi)存泄露了?
因為匿名內(nèi)部類是靜態(tài)的之后,它所引用的對象或?qū)傩砸脖仨毷庆o態(tài)的了,因此就可以直接從 JVM 的 Method Area(方法區(qū))獲取到引用而無需持久外部對象了。
代碼
package org.example.a;
import java.util.ArrayList;
import java.util.List;
class Test{
public static List<String> createList() {
List<String> list = new ArrayList<String>() {{
add("a");
add("b");
}};
return list;
}
}
public class Demo {
public static void main(String[] args) {
System.out.println(Test.createList());
}
}執(zhí)行結(jié)果
[a, b]
編譯
命令:javac Demo.java
結(jié)果

Idea查看Test$1.class
package org.example.a;
import java.util.ArrayList;
final class Test$1 extends ArrayList<String> {
Test$1() {
this.add("a");
this.add("b");
}
}到此這篇關(guān)于Java匿名內(nèi)部類導致內(nèi)存泄露的原因與解決方案詳解的文章就介紹到這了,更多相關(guān)Java內(nèi)存泄露內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring MVC登錄注冊以及轉(zhuǎn)換json數(shù)據(jù)
本文主要介紹了Spring MVC登錄注冊以及轉(zhuǎn)換json數(shù)據(jù)的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04
SpringBoot集成quartz實現(xiàn)定時任務
這篇文章主要介紹了如何使用SpringBoot整合Quartz,并將定時任務寫入庫中(持久化存儲),還可以任意對定時任務進行如刪除、暫停、恢復等操作,需要的可以了解下2023-09-09
Spring Cloud Sleuth整合zipkin過程解析
這篇文章主要介紹了Spring Cloud Sleuth整合zipkin過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-12-12
JDBC PreparedStatement Like參數(shù)報錯解決方案
這篇文章主要介紹了JDBC PreparedStatement Like參數(shù)報錯解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-10-10

