java 内存泄漏_Java避免内部类中的内存泄漏

8a0de1b701aed9bf3d516a99db58960d.png

内部类有在JVM中导致内存泄漏和内存不足错误的可能性。

之所以会发生这种类型的内存泄漏,是因为内部类必须始终能够访问其外部类-其实这并非JVM期望的。

从简单的嵌套过程到内存不足错误(并可能关闭JVM)是​​一个过程。理解它的最好方法是观察它的发展。

步骤1:内部类引用其外部类

内部类的任何实例都包含对其外部类的隐式引用。例如,考虑以下EnclosingClass带有嵌套的EnclosedClass非静态成员类的声明:

public class EnclosingClass{   public class EnclosedClass   {   }}

为了更好地理解这种联系,我们可以将上面的源代码(javac EnclosingClass.java)编译为EnclosingClass.class和EnclosingClass$EnclosedClass.class,然后检查后者的类文件

javap EnclosingClass$EnclosedClass

应该观察以下输出,该输出显示了包含以下内容的 final EnclosingClass this$0包含字段EnclosingClass的引用。

Compiled from "EnclosingClass.java"public class EnclosingClass$EnclosedClass {  final EnclosingClass this$0;  public EnclosingClass$EnclosedClass(EnclosingClass);}

步骤2:构造函数获取封闭的类引用

上面的输出显示了带有EnclosingClass参数的构造函数。javap使用-v(详细)选项执行,您将观察到构造函数将EnclosingClass对象引用保存在this$0字段中:

final EnclosingClass this$0;  descriptor: LEnclosingClass;  flags: (0x1010) ACC_FINAL, ACC_SYNTHETICpublic EnclosingClass$EnclosedClass(EnclosingClass);  descriptor: (LEnclosingClass;)V  flags: (0x0001) ACC_PUBLIC  Code:    stack=2, locals=2, args_size=2       0: aload_0       1: aload_1       2: putfield      #1                  // Field this$0:LEnclosingClass;       5: aload_0       6: invokespecial #2                  // Method java/lang/Object."":()V       9: return    LineNumberTable:      line 3: 0

步骤3:声明一种新方法

接下来,假设在另一个实例化的类中声明了一个方法EnclosingClass,然后是EnclosedClass。下一个代码片段揭示了此实例化序列:

EnclosingClass ec = new EnclosingClass();ec.new EnclosedClass();

以下javap输出显示了此源代码的字节码转换。第18行显示了对的调用EnclosingClass$EnclosedClass(EnclosingClass)。该调用是为了将enclosing类引用保存在enclosing类中:

0: new           #2 // class EnclosingClass 3: dup 4: invokespecial #3 // Method EnclosingClass."":()V 7: astore_1 8: new           #4 // class EnclosingClass$EnclosedClass11: dup12: aload_113: dup14: invokestatic  #5 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;17: pop18: invokespecial #6 // Method EnclosingClass$EnclosedClass."":(LEnclosingClass;)V21: pop22: return

内存泄漏的解剖

在以上示例中,我们已将封闭类的引用存储在封闭类的制造变量中。这可能导致内存泄漏,其中的封闭类引用了无法垃圾回收的大型对象图。根据应用程序代码,可能会耗尽内存并收到内存不足错误,从而导致JVM终止。下面的代码演示了这种情况。

import java.util.ArrayList;class EnclosingClass{   private int[] data;   public EnclosingClass(int size)   {      data = new int[size];   }   class EnclosedClass   {   }   EnclosedClass getEnclosedClassObject()   {      return new EnclosedClass();   }}public class MemoryLeak{   public static void main(String[] args)   {      ArrayList al = new ArrayList<>();      int counter = 0;      while (true)      {         al.add(new EnclosingClass(100000).getEnclosedClassObject());         System.out.println(counter++);      }   }}

该EnclosingClass声明一个私有data引用整数数组领域。数组的大小传递给此类的构造函数,并实例化该数组。

EnclosingClass还声明EnclosedClass,一个嵌套非静态成员的类,和一种方法,其实例化EnclosedClass,返回此实例

MemoryLeak的main()方法首先创建一个java.util.ArrayList存储EnclosingClass.EnclosedClass对象,开始观察内存泄漏

将计数器初始化为0后,main()进入无限while循环,该循环重复实例化EnclosedClass并将其添加到数组列表中。然后打印(或递增)计数器。在实例化封闭的类之前,EnclosingClass必须实例化该实例,并将100000其作为数组大小传递

每个存储的EnclosedClass对象维护对其封闭对象的引用,该对象引用由100,000个32位整数(或400,000字节)组成的数组。在对内部对象进行垃圾收集之前,无法对外部对象进行垃圾收集。最终,该应用程序将耗尽内存

7639764076417642764376447645Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat EnclosingClass.(MemoryLeak.java:9)at MemoryLeak.main(MemoryLeak.java:30)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值