Android内存泄漏的场景

寸阳分阴须爱惜,休负春色与时光。这篇文章主要讲述Android内存泄漏的场景 相关的知识,希望能为你提供帮助。
场景一:匿名内部类、非静态内部类 隐式持有外部类的引用
非静态内部类示例

1 public class Test { 2private int testValue = https://www.songbingjia.com/android/1; 3 4public class Outer { 5private int outerValue = 2; 6 7public class Inner { 8private int innerValue = 3; 9 10public void print() { 11System.out.println("testValue="https://www.songbingjia.com/android/+ testValue +", outerValue="https://www.songbingjia.com/android/+ outerValue 12+", innerValue="https://www.songbingjia.com/android/+ innerValue); 13} 14} 15} 16 17public static void main(String[] args) { 18Test test = new Test(); 19Outer outer = test.new Outer(); 20Outer.Inner inner = outer.new Inner(); 21inner.print(); 22} 23 }

如19-20行所示,在外部类外实例化非静态内部类时,需要通过外部类的实例.new进行构造。
使用 javac Test.java进行编译,生成Test$Outer$Inner.class、Test$Outer.class、Test.class几个class文件。使用javap -p class_file_name查看,结果如下:
? ~/Programming/javajavap -p "Test.class" Compiled from "Test.java" public class Test { private int testValue; public Test(); public static void main(java.lang.String[]); static int access$000(Test); } ? ~/Programming/javajavap -p "Test\$Outer.class" Compiled from "Test.java" public class Test$Outer { private int outerValue; final Test this$0; public Test$Outer(Test); static int access$100(Test$Outer); } ? ~/Programming/javajavap -p "Test\$Outer\$Inner.class" Compiled from "Test.java" public class Test$Outer$Inner { private int innerValue; final Test$Outer this$1; public Test$Outer$Inner(Test$Outer); public void print(); }

  从生成的class文件可以看出,内部类的构造函数被自动加上以外部类对象为参数,并且内部类有一个成员指向一个外部类对象(Test$Outer的this$0,Test$Outer$Inner的this$1)。
外部类和内部类之间可以相互访问对方的私有属性,而编译之后每个类都是单独的class文件,因此编译器会自动生成相应的包访问权限的静态方法(只有在需要的时候才会生成),来支持这种私有属性的访问(Test中为testValue生成的access$000静态方法,Test$Outer中为outerValue生成的access$100静态方法)。
匿名内部类示例
1 public class AnonymousTest { 2private int testValue = https://www.songbingjia.com/android/1; 3 4public interface IAnonymous { 5void print(); 6} 7 8private final IAnonymous anonymous = new IAnonymous() { 9private int innerValue = 2; 10 11public void print() { 12System.out.println("testValue="https://www.songbingjia.com/android/+ testValue +", innerValue="https://www.songbingjia.com/android/+ innerValue); 13} 14}; 15 16public static void main(String[] args) { 17AnonymousTest test = new AnonymousTest(); 18test.anonymous.print(); 19} 20 }

使用 javac  AnonymousTest.java进行编译,生成AnonymousTest$1.class、AnonymousTest$IAnonymous.class、AnonymousTest.class几个class文件。使用javap -p class_file_name查看,结果如下:
? ~/Programming/javajavap -p AnonymousTest.class Compiled from "AnonymousTest.java" public class AnonymousTest { private int testValue; private final AnonymousTest$IAnonymous anonymous; public AnonymousTest(); public static void main(java.lang.String[]); static int access$000(AnonymousTest); } ? ~/Programming/javajavap -p "AnonymousTest\$IAnonymous.class" Compiled from "AnonymousTest.java" public interface AnonymousTest$IAnonymous { public abstract void print(); } ? ~/Programming/javajavap -p "AnonymousTest\$1.class" Compiled from "AnonymousTest.java" class AnonymousTest$1 implements AnonymousTest$IAnonymous { private int innerValue; final AnonymousTest this$0; AnonymousTest$1(AnonymousTest); public void print(); }

从生成的class文件可以看出,匿名内部类与非静态内部类一样,同样持有外部类的引用。不同之处在于,匿名内部类的名字是外部类$数字。匿名内部类可以定义成员变量和成员函数,但是只能在本类中访问,也无法通过JAVA代码直接构造匿名内部类的新的实例。
结论
【Android内存泄漏的场景】由于非静态内部类和匿名内部类的实例会隐式持有外部类实例的引用,因此,若其生命周期比外部类实例长,会导致外部类实例无法回收而产生泄漏。因此,在使用要格外注意。

    推荐阅读