文章目录

  • 避免内部类中的内存泄漏
  • 步骤1:内部类引用其外部类
  • 步骤2:构造函数获取封闭的类引用
  • 步骤3:声明一种新方法
  • 内存泄漏的解剖

避免内部类中的内存泄漏

使用内部类时要当心垃圾收集
如果您已了解静态类和内部类,则应该熟悉使用Java代码中的嵌套类的基础知识。在这个相关的技巧中,我将带您了解嵌套类的陷阱之一,这是内部类在JVM中导致内存泄漏和内存不足错误的潜力。

之所以会发生这种类型的内存泄漏,是因为内部类必须始终能够访问其外部类-并非总是与JVM的计划一起使用。

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

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

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

public class EnclosingClass
{public class EnclosedClass{}
}

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

JDK包含一个javap(Java打印)工具,用于反汇编类文件。在命令行上,使用javap运行EnclosingClass$EnclosedClass如下:

javap EnclosingClass$EnclosedClass

您可以看到以下输出,该输出显示了包含以下内容final EnclosingClass this$0字段EnclosingClass:

public class com.github.crab2died.EnclosingClass$EnclosedClass {final com.github.crab2died.EnclosingClass this$0;public com.github.crab2died.EnclosingClass$EnclosedClass(com.github.crab2died.EnclosingClass);
}

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

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

Classfile /D:/aplus/EnclosingClass$EnclosedClass.classLast modified 2020-3-15; size 440 bytesMD5 checksum 308ea24edb49a4d49669d101fff55d5aCompiled from "EnclosingClass.java"
public class com.github.crab2died.EnclosingClass$EnclosedClassminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Fieldref           #3.#13         // com/github/crab2died/EnclosingClass$EnclosedClass.this$0:Lcom/github/crab2died/EnclosingClass;#2 = Methodref          #4.#14         // java/lang/Object."<init>":()V#3 = Class              #16            // com/github/crab2died/EnclosingClass$EnclosedClass#4 = Class              #19            // java/lang/Object#5 = Utf8               this$0#6 = Utf8               Lcom/github/crab2died/EnclosingClass;#7 = Utf8               <init>#8 = Utf8               (Lcom/github/crab2died/EnclosingClass;)V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               SourceFile#12 = Utf8               EnclosingClass.java#13 = NameAndType        #5:#6          // this$0:Lcom/github/crab2died/EnclosingClass;#14 = NameAndType        #7:#20         // "<init>":()V#15 = Class              #21            // com/github/crab2died/EnclosingClass#16 = Utf8               com/github/crab2died/EnclosingClass$EnclosedClass#17 = Utf8               EnclosedClass#18 = Utf8               InnerClasses#19 = Utf8               java/lang/Object#20 = Utf8               ()V#21 = Utf8               com/github/crab2died/EnclosingClass
{final com.github.crab2died.EnclosingClass this$0;descriptor: Lcom/github/crab2died/EnclosingClass;flags: ACC_FINAL, ACC_SYNTHETICpublic com.github.crab2died.EnclosingClass$EnclosedClass(com.github.crab2died.EnclosingClass);descriptor: (Lcom/github/crab2died/EnclosingClass;)Vflags: ACC_PUBLICCode:stack=2, locals=2, args_size=20: aload_01: aload_12: putfield      #1                  // Field this$0:Lcom/github/crab2died/EnclosingClass;5: aload_06: invokespecial #2                  // Method java/lang/Object."<init>":()V9: returnLineNumberTable:line 12: 0
}
SourceFile: "EnclosingClass.java"
InnerClasses:public #17= #3 of #15; //EnclosedClass=class com/github/crab2died/EnclosingClass$EnclosedClass of class com/github/crab2died/EnclosingClass

步骤3:声明一种新方法

在实例化类中声明了EnclosingClass,然后创建EnclosedClass。

EnclosingClass ec = new EnclosingClass();
ec.new EnclosedClass();
 Last modified 2020-3-15; size 502 bytesMD5 checksum d31832f98dbbf557e995ac447cc55fb2Compiled from "EnclosingClass.java"
public class com.github.crab2died.EnclosingClassminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #7.#17         // java/lang/Object."<init>":()V#2 = Class              #18            // com/github/crab2died/EnclosingClass#3 = Methodref          #2.#17         // com/github/crab2died/EnclosingClass."<init>":()V#4 = Class              #19            // com/github/crab2died/EnclosingClass$EnclosedClass#5 = Methodref          #7.#20         // java/lang/Object.getClass:()Ljava/lang/Class;#6 = Methodref          #4.#21         // com/github/crab2died/EnclosingClass$EnclosedClass."<init>":(Lcom/github/crab2died/EnclosingClass;)V#7 = Class              #22            // java/lang/Object#8 = Utf8               EnclosedClass#9 = Utf8               InnerClasses#10 = Utf8               <init>#11 = Utf8               ()V#12 = Utf8               Code#13 = Utf8               LineNumberTable#14 = Utf8               test#15 = Utf8               SourceFile#16 = Utf8               EnclosingClass.java#17 = NameAndType        #10:#11        // "<init>":()V#18 = Utf8               com/github/crab2died/EnclosingClass#19 = Utf8               com/github/crab2died/EnclosingClass$EnclosedClass#20 = NameAndType        #23:#24        // getClass:()Ljava/lang/Class;#21 = NameAndType        #10:#25        // "<init>":(Lcom/github/crab2died/EnclosingClass;)V#22 = Utf8               java/lang/Object#23 = Utf8               getClass#24 = Utf8               ()Ljava/lang/Class;#25 = Utf8               (Lcom/github/crab2died/EnclosingClass;)V
{public com.github.crab2died.EnclosingClass();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 10: 0public void test();descriptor: ()Vflags: ACC_PUBLICCode:stack=4, locals=2, args_size=10: new           #2                  // class com/github/crab2died/EnclosingClass3: dup4: invokespecial #3                  // Method "<init>":()V7: astore_18: new           #4                  // class com/github/crab2died/EnclosingClass$EnclosedClass11: dup12: aload_113: dup14: invokevirtual #5                  // Method java/lang/Object.getClass:()Ljava/lang/Class;17: pop18: invokespecial #6                  // Method com/github/crab2died/EnclosingClass$EnclosedClass."<init>":(Lcom/github/crab2died/EnclosingClass;)V21: pop22: returnLineNumberTable:line 17: 0line 18: 8line 19: 22
}
SourceFile: "EnclosingClass.java"
InnerClasses:public #8= #4 of #2; //EnclosedClass=class com/github/crab2died/EnclosingClass$EnclosedClass of class com/github/crab2died/EnclosingClass

内存泄漏的解剖

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

MemoryLeak.java

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对象。暂时不使用包和泛型以及将包和泛型ArrayList(将对象存储在动态数组中)的使用-重要的一点是观察内存泄漏是如何发生的。

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

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

我观察到输出的以下后缀-请注意,您可能会观察到不同的最终计数器值:


7639
7640
7641
7642
7643
7644
7645
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat EnclosingClass.<init>(MemoryLeak.java:9)at MemoryLeak.main(MemoryLeak.java:30)

详细分析内部类的发生内存泄漏的原因相关推荐

  1. Android 中内存泄漏的原因和解决方案

    之前研究过一段时间关于 Android 内存泄漏的知识,大致了解了导致内存泄漏的一些原因,但是没有深入去探究,很多细节也理解的不够透彻,基本上处于一种似懂非懂的状态,最近又研究了一波,发现有很多新的收 ...

  2. 在使用SPSite对象时容易发生内存泄漏

    如题所说,在使用SPSite对象时容易内存泄漏!造成内存泄漏的原因是没有正确地关闭SPSite对象,请大家Review一下代码,及时修正! 项目中,对系统进行压力测试时,出现了大量的异常信息,类似如下 ...

  3. UWP开发入门(十六)——常见的内存泄漏的原因

    原文:UWP开发入门(十六)--常见的内存泄漏的原因 本篇借鉴了同事翔哥的劳动成果,在巨人的肩膀上把稿子又念了一遍. 内存泄漏的概念我这里就不说了,之前<UWP开发入门(十三)--用Diagno ...

  4. 内存泄漏的原因及解决办法_编程基础 | C++片段 指针、多态和内存分配

    本片段将介绍运行期而不是编译期的内存分配 1.变量的内存分配和方法的前期绑定 函数中声明的局部变量与其参数以及簿记数据一起被放置在一个活动记录中.活动记录存储在名为运行期栈(run-time stac ...

  5. 内存泄漏的原因及解决办法_内存泄漏的场景和解决办法

    1.非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类 2.多线程相关的匿名内部类和非静态内部类 匿名内部类同样会持 ...

  6. new arraylist内存_如何避免内部类中的内存泄漏

    我先假设读者已经熟悉在Java代码中使用嵌套类的基础知识.在本文里,我将展示嵌套类的陷阱,内部类在JVM中引起内存泄漏和内存不足错误的地方.之所以会发生这种类型的内存泄漏,是因为内部类必须始终能够访问 ...

  7. java final 内存泄漏_干货详解:一文教你如何避免内部类中的内存泄漏

    我先假设读者已经熟悉在Java代码中使用嵌套类的基础知识.在本文里,我将展示嵌套类的陷阱,内部类在JVM中引起内存泄漏和内存不足错误的地方.之所以会发生这种类型的内存泄漏,是因为内部类必须始终能够访问 ...

  8. Java 中发生内存泄漏 5 个场景以及解决方法

    前言 说到垃圾回收(Garbage Collection,GC),很多人就会自然而然地把它和 Java 联系起来.在 Java 中,程序员不需要去关心内存动态分配和垃圾回收的问题,这一切都交给了JVM ...

  9. 如何分析EFCore引发的内存泄漏

    调查实体框架核心中的内存泄漏 不要让内存泄漏成为洪水 术语"内存泄漏"和" .NET应用程序"不是经常一起使用.但是,我们最近在一个.NET Core Web应 ...

最新文章

  1. 使用 Gitlab 进行嵌入式软件开发技巧
  2. 爱立信华为MWC展前叫阵:5G成重头戏
  3. Python中dict用法详解
  4. 数据结构与算法-----冒泡排序
  5. 博弈论之软件测试的价值
  6. 路由表,路由,路由规则_路由和路由表简介
  7. C语言中函数调用中的传值与传址
  8. linux上的定时器上的jiffies,Linux kernel -- 定时器/jiffies
  9. JZOJ1286太空电梯
  10. Microsoft visio 2010 Premium 的激活
  11. pygarm windows 安装_飘云阁(PYG官方) Windows PowerShell实战指南(第2版)PDF - Powered by Discuz!...
  12. WebRTC自适应控制算法
  13. 服务器挂硬盘 BIOS,TaiShan服务器通过BIOS格式化硬盘将硬盘中的残留数据擦除操作方法...
  14. html5+ mui框架 微信授权登录无响应,不回跳APP
  15. 视觉SLAM十四讲从理论到实践第二版源码调试笔记(理论基础1-6章)
  16. 亚马逊多店铺统一管理还不会关联?原来是靠它
  17. 赶紧收藏3个免费在线资源齐全的网站
  18. 彻底解决Word中子标题变黑块问题
  19. 关于魔趣刷机(含root)步骤
  20. 解决 这些文件可能对你的计算机有害问题

热门文章

  1. xlwt什么版本支持xlsx的版本_Spring Framework Versions版本支持说明
  2. lingo变量无限制版本_LINGO笔记
  3. 重庆计算机学校电话号码,重庆计算机学校
  4. linux带字符的行,linux小计,统计文件中包含指定字符串的行数
  5. AI理论知识整理(2)-对称矩阵-特征值与特征向量
  6. vb.net2019- 串口serial port通信
  7. 【Python】如何用Python来操作PDF文件,建议收藏
  8. 【数据竞赛】Kaggle实战之单类别变量特征工程总结!
  9. 【Python基础】Python的元组,没想象的那么简单
  10. 【竞赛总结】新冠期间饿了么骑士行为预估