0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java内部类持有外部类导致内存泄露的原因以及其解决方案

电子工程师 来源: 芋道源码 作者: 芋道源码 2022-10-08 16:32 次阅读

简介

为什么要持有外部类

实例:持有外部类

实例:不持有外部类

实例:内存泄露

不会内存泄露的方案

简介

「说明」

本文介绍 Java 内部类持有外部类导致内存泄露的原因以及其解决方案。

「为什么内部类持有外部类会导致内存泄露?」

非静态内部类会持有外部类,如果有地方引用了这个非静态内部类,会导致外部类也被引用,垃圾回收时无法回收这个外部类(即使外部类已经没有其他地方在使用了)。

「解决方案」

不要让其他的地方持有这个非静态内部类的引用,直接在这个非静态内部类执行业务。

将非静态内部类改为静态内部类。内部类改为静态的之后,它所引用的对象或属性也必须是静态的,所以静态内部类无法获得外部对象的引用,只能从 JVM 的 Method Area(方法区)获取到static类型的引用。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

为什么要持有外部类

Java 语言中,非静态内部类的主要作用有两个:

当内部类只在外部类中使用时,匿名内部类可以让外部不知道它的存在,从而减少了代码的维护工作。

当内部类持有外部类时,它就可以直接使用外部类中的变量了,这样可以很方便的完成调用,如下代码所示:

packageorg.example.a;

classOuter{
privateStringouterName="Tony";

classInner{
privateStringname;

publicInner(){
this.name=outerName;
}
}

InnercreateInner(){
returnnewInner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Outer.Innerinner=newOuter().createInner();
System.out.println(inner);
}
}

但是,静态内部类就无法持有外部类和其非静态字段了。比如下边这样就会报错

packageorg.example.a;

classOuter{
privateStringouterName="Tony";

staticclassInner{
privateStringname;

publicInner(){
this.name=outerName;
}
}

InnercreateInner(){
returnnewInner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Outer.Innerinner=newOuter().createInner();
System.out.println(inner);
}
}

报错:

9985cd7a-46ae-11ed-96c9-dac502259ad0.png

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://gitee.com/zhijiantianya/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

实例:持有外部类

「代码」

packageorg.example.a;

classOuter{
classInner{

}

InnercreateInner(){
returnnewInner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Outer.Innerinner=newOuter().createInner();
System.out.println(inner);
}
}

「断点调试」

可以看到:内部类持有外部类的对象的引用,是以“this$0”这个字段来保存的。

99a23ec4-46ae-11ed-96c9-dac502259ad0.png

实例:不持有外部类

「代码」

packageorg.example.a;

classOuter{
staticclassInner{

}

InnercreateInner(){
returnnewInner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Outer.Innerinner=newOuter().createInner();
System.out.println(inner);
}
}

「断点调试」

可以发现:内部类不再持有外部类了。

99c4316e-46ae-11ed-96c9-dac502259ad0.png

实例:内存泄露

「简介」

若内部类持有外部类的引用,对内部类的使用很多时,会导致外部类数目很多。此时,就算是外部类的数据没有被用到,外部类的数据所占空间也不会被释放。

本处在外部类存放大量的数据来模拟

「代码」

packageorg.example.a;

importjava.util.ArrayList;
importjava.util.List;

classOuter{
privateint[]data;

publicOuter(intsize){
this.data=newint[size];
}

classInnner{

}

InnnercreateInner(){
returnnewInnner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Listlist=newArrayList<>();
intcounter=0;
while(true){
list.add(newOuter(100000).createInner());
System.out.println(counter++);
}
}
}

「测试」

可以看到:运行了八千多次的时候就内存溢出了。

99d10b96-46ae-11ed-96c9-dac502259ad0.png

换了一台 mac 电脑,4000 多就内存溢出了。

99f6a4c8-46ae-11ed-96c9-dac502259ad0.png

不会内存泄露的方案

「简介」

内部类改为静态的之后,它所引用的对象或属性也必须是静态的,所以静态内部类无法获得外部对象的引用,只能从 JVM 的 Method Area(方法区)获取到 static 类型的引用。

「代码」

packageorg.example.a;

importjava.util.ArrayList;
importjava.util.List;

classOuter{
privateint[]data;

publicOuter(intsize){
this.data=newint[size];
}

staticclassInner{

}

InnercreateInner(){
returnnewInner();
}
}

publicclassDemo{
publicstaticvoidmain(String[]args){
Listlist=newArrayList<>();
intcounter=0;
while(true){
list.add(newOuter(100000).createInner());
System.out.println(counter++);
}
}
}

「测试」

可以发现:循环了四十多万次都没有内存溢出。

9a1f2cfe-46ae-11ed-96c9-dac502259ad0.png

以上,希望能对大家在使用内部类时会有所帮助。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    2996

    浏览量

    73867
  • JAVA
    +关注

    关注

    19

    文章

    2956

    浏览量

    104530
  • JVM
    JVM
    +关注

    关注

    0

    文章

    157

    浏览量

    12205
  • static
    +关注

    关注

    0

    文章

    33

    浏览量

    10356

原文标题:Java内部类有坑,100%内存泄露!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Java知识:内部类与匿名类_内部类#Java

    JAVA
    学习电子
    发布于 :2022年11月16日 01:30:13

    Java基础知识:内部类和匿名内部类#Java

    JAVA
    学习硬声知识
    发布于 :2022年11月16日 17:55:25

    java rules下载/java规范中文版

    使用SDK创建包2.5 导入声明2.5.1 自动导入2.5.2 冗余导入2.5.3 各种嵌套类的导入(顶层类或内部类)2.5.4 编译器对导入语句的解析2.5.5 按需类型导入声明的效能2.6 类型声明
    发表于 06-23 18:16

    java基础知识精讲视频教程百度云盘分享!

    日历57 java日期58 Math与Random59 内部类60 单例模式61 工厂模式62 线程的创建63 使用runnable创建线程64 tcp编程一65 网络编程二66 对象的clone67
    发表于 07-12 11:50

    ARM之静态变量简析

    静态变量的值可以一直保持到程序结束,使用关键字static可以将变量声明为静态的:static int x ;static float y;根据声明的位置不同,静态变量也可以分为内部类型或外部类
    发表于 04-24 09:27

    对在Firefly-RK3288开发板上的Hello进行内存泄漏分析常见实例

    、GiftLetl. regNewMsgHandler后,在退出fragment后虽然手动设为null,但是因为原来那个已经设置另一进程而导致内存泄漏实例4、内部类中使用宿主类变量(截图中是ListView的Adapter),而
    发表于 09-22 14:46

    接口、内部类Java API基础

    理解接口的作用,理解接口和实现接口的类的关系,掌握声明接口、一个类实现多个接口的声明和使用方法。理解内嵌类型的概念,掌握声明内部类的方法。熟悉Java语言包和实用包中的常用类。
    发表于 11-23 11:52 0次下载

    java内部类分析详解

    可以将一个类的定义放在另一个类的定义内部,这就是内部类内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对
    发表于 09-27 14:59 0次下载

    java内部类怎么写

    一。内部类基础 二。深入理解内部类 三。内部类的使用场景和好处 四。常见的与内部类相关的笔试面试题 若有不正之处,请多谅解并欢迎批评指正。 一。内部
    发表于 09-27 16:15 0次下载
    <b class='flag-5'>java</b><b class='flag-5'>内部类</b>怎么写

    内存溢出和内存泄露的区别_内存溢出的原因以及解决方法

    内存溢出和内存泄露的区别是什么?内存溢出怎么解决?内存溢出是指程序在申请内存时,没有足够的
    发表于 06-01 10:27 2890次阅读

    Java内部类使用不当导致内存泄露问题及解决办法

    非静态内部类持有外部类,如果有地方引用了这个非静态内部类,会导致外部类也被引用,垃圾回收时无法
    的头像 发表于 05-18 16:09 1099次阅读
    <b class='flag-5'>Java</b><b class='flag-5'>内部类</b>使用不当<b class='flag-5'>导致</b>的<b class='flag-5'>内存</b><b class='flag-5'>泄露</b>问题及解决办法

    static定义内部类

    1. static定义内部类 1.1 static定义内部类 如果说现在内部类上使用了static定义,那么这个内部类就变成了“外部类”,s
    的头像 发表于 10-10 16:08 524次阅读

    如何在普通类中使用内部结构

    class Test { public static void main ( String [] args ) { Outer outer = new Outer(); outer.fun(); }} 创建外部类的实例调用外部类的方法却执行了
    的头像 发表于 10-10 16:26 404次阅读

    java内部类可以用public修饰吗

    使用任意访问权限修饰符,包括public、protected、默认和private。 方法内部类只能使用默认访问权限,即不加任何访问修饰符。 使用public修饰的内部类 内部类使用public修饰时,可以被
    的头像 发表于 11-21 10:23 1082次阅读

    java内存溢出的几种原因和解决办法

    内存,但是如果程序中存在内存泄漏(Memory Leak)或者使用不当的数据结构等问题,仍然有可能导致内存溢出。下面将详细介绍Java
    的头像 发表于 11-23 14:44 6034次阅读