0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Java內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因以及其解決方案

電子工程師 ? 來源: 芋道源碼 ? 作者: 芋道源碼 ? 2022-10-08 16:32 ? 次閱讀

簡介

為什么要持有外部類

實例:持有外部類

實例:不持有外部類

實例:內(nèi)存泄露

不會內(nèi)存泄露的方案

簡介

「說明」

本文介紹 Java 內(nèi)部類持有外部類導(dǎo)致內(nèi)存泄露的原因以及其解決方案。

「為什么內(nèi)部類持有外部類會導(dǎo)致內(nèi)存泄露?」

非靜態(tài)內(nèi)部類會持有外部類,如果有地方引用了這個非靜態(tài)內(nèi)部類,會導(dǎo)致外部類也被引用,垃圾回收時無法回收這個外部類(即使外部類已經(jīng)沒有其他地方在使用了)。

「解決方案」

不要讓其他的地方持有這個非靜態(tài)內(nèi)部類的引用,直接在這個非靜態(tài)內(nèi)部類執(zhí)行業(yè)務(wù)。

將非靜態(tài)內(nèi)部類改為靜態(tài)內(nèi)部類。內(nèi)部類改為靜態(tài)的之后,它所引用的對象或?qū)傩砸脖仨毷庆o態(tài)的,所以靜態(tài)內(nèi)部類無法獲得外部對象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到static類型的引用。

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

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

視頻教程:https://doc.iocoder.cn/video/

為什么要持有外部類

Java 語言中,非靜態(tài)內(nèi)部類的主要作用有兩個:

當(dāng)內(nèi)部類只在外部類中使用時,匿名內(nèi)部類可以讓外部不知道它的存在,從而減少了代碼的維護(hù)工作。

當(dāng)內(nèi)部類持有外部類時,它就可以直接使用外部類中的變量了,這樣可以很方便的完成調(diào)用,如下代碼所示:

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);
}
}

但是,靜態(tài)內(nèi)部類就無法持有外部類和其非靜態(tài)字段了。比如下邊這樣就會報錯

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 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

項目地址: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);
}
}

「斷點調(diào)試」

可以看到:內(nèi)部類持有外部類的對象的引用,是以“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);
}
}

「斷點調(diào)試」

可以發(fā)現(xiàn):內(nèi)部類不再持有外部類了。

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

實例:內(nèi)存泄露

「簡介」

若內(nèi)部類持有外部類的引用,對內(nèi)部類的使用很多時,會導(dǎo)致外部類數(shù)目很多。此時,就算是外部類的數(shù)據(jù)沒有被用到,外部類的數(shù)據(jù)所占空間也不會被釋放。

本處在外部類存放大量的數(shù)據(jù)來模擬。

「代碼」

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++);
}
}
}

「測試」

可以看到:運行了八千多次的時候就內(nèi)存溢出了。

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

換了一臺 mac 電腦,4000 多就內(nèi)存溢出了。

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

不會內(nèi)存泄露的方案

「簡介」

內(nèi)部類改為靜態(tài)的之后,它所引用的對象或?qū)傩砸脖仨毷庆o態(tài)的,所以靜態(tài)內(nèi)部類無法獲得外部對象的引用,只能從 JVM 的 Method Area(方法區(qū))獲取到 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++);
}
}
}

「測試」

可以發(fā)現(xiàn):循環(huán)了四十多萬次都沒有內(nèi)存溢出。

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

以上,希望能對大家在使用內(nèi)部類時會有所幫助。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    3025

    瀏覽量

    74056
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2967

    瀏覽量

    104758
  • JVM
    JVM
    +關(guān)注

    關(guān)注

    0

    文章

    158

    瀏覽量

    12228
  • static
    +關(guān)注

    關(guān)注

    0

    文章

    33

    瀏覽量

    10372

原文標(biāo)題:Java內(nèi)部類有坑,100%內(nèi)存泄露!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Java知識:內(nèi)部類與匿名類_內(nèi)部類#Java

    JAVA
    學(xué)習(xí)電子
    發(fā)布于 :2022年11月16日 01:30:13

    Java基礎(chǔ)知識:內(nèi)部類和匿名內(nèi)部類#Java

    JAVA
    學(xué)習(xí)硬聲知識
    發(fā)布于 :2022年11月16日 17:55:25

    java rules下載/java規(guī)范中文版

    使用SDK創(chuàng)建包2.5 導(dǎo)入聲明2.5.1 自動導(dǎo)入2.5.2 冗余導(dǎo)入2.5.3 各種嵌套類的導(dǎo)入(頂層類或內(nèi)部類)2.5.4 編譯器對導(dǎo)入語句的解析2.5.5 按需類型導(dǎo)入聲明的效能2.6 類型聲明
    發(fā)表于 06-23 18:16

    java基礎(chǔ)知識精講視頻教程百度云盤分享!

    日歷57 java日期58 Math與Random59 內(nèi)部類60 單例模式61 工廠模式62 線程的創(chuàng)建63 使用runnable創(chuàng)建線程64 tcp編程一65 網(wǎng)絡(luò)編程二66 對象的clone67
    發(fā)表于 07-12 11:50

    ARM之靜態(tài)變量簡析

    靜態(tài)變量的值可以一直保持到程序結(jié)束,使用關(guān)鍵字static可以將變量聲明為靜態(tài)的:static int x ;static float y;根據(jù)聲明的位置不同,靜態(tài)變量也可以分為內(nèi)部類型或外部類
    發(fā)表于 04-24 09:27

    對在Firefly-RK3288開發(fā)板上的Hello進(jìn)行內(nèi)存泄漏分析常見實例

    、GiftLetl. regNewMsgHandler后,在退出fragment后雖然手動設(shè)為null,但是因為原來那個已經(jīng)設(shè)置另一進(jìn)程而導(dǎo)致內(nèi)存泄漏實例4、內(nèi)部類中使用宿主類變量(截圖中是ListView的Adapter),而
    發(fā)表于 09-22 14:46

    接口、內(nèi)部類Java API基礎(chǔ)

    理解接口的作用,理解接口和實現(xiàn)接口的類的關(guān)系,掌握聲明接口、一個類實現(xiàn)多個接口的聲明和使用方法。理解內(nèi)嵌類型的概念,掌握聲明內(nèi)部類的方法。熟悉Java語言包和實用包中的常用類。
    發(fā)表于 11-23 11:52 ?0次下載

    java內(nèi)部類分析詳解

    可以將一個類的定義放在另一個類的定義內(nèi)部,這就是內(nèi)部類。 內(nèi)部類是一個非常有用的特性但又比較難理解使用的特性(鄙人到現(xiàn)在都沒有怎么使用過內(nèi)部類,對內(nèi)
    發(fā)表于 09-27 14:59 ?0次下載

    java內(nèi)部類怎么寫

    一。內(nèi)部類基礎(chǔ) 二。深入理解內(nèi)部類 三。內(nèi)部類的使用場景和好處 四。常見的與內(nèi)部類相關(guān)的筆試面試題 若有不正之處,請多諒解并歡迎批評指正。 一。內(nèi)部
    發(fā)表于 09-27 16:15 ?0次下載
    <b class='flag-5'>java</b><b class='flag-5'>內(nèi)部類</b>怎么寫

    內(nèi)存溢出和內(nèi)存泄露的區(qū)別_內(nèi)存溢出的原因以及解決方法

    內(nèi)存溢出和內(nèi)存泄露的區(qū)別是什么?內(nèi)存溢出怎么解決?內(nèi)存溢出是指程序在申請內(nèi)存時,沒有足夠的
    發(fā)表于 06-01 10:27 ?2917次閱讀

    Java內(nèi)部類使用不當(dāng)導(dǎo)致內(nèi)存泄露問題及解決辦法

    非靜態(tài)內(nèi)部類持有外部類,如果有地方引用了這個非靜態(tài)內(nèi)部類,會導(dǎo)致外部類也被引用,垃圾回收時無法
    的頭像 發(fā)表于 05-18 16:09 ?1137次閱讀
    <b class='flag-5'>Java</b><b class='flag-5'>內(nèi)部類</b>使用不當(dāng)<b class='flag-5'>導(dǎo)致</b>的<b class='flag-5'>內(nèi)存</b><b class='flag-5'>泄露</b>問題及解決辦法

    static定義內(nèi)部類

    1. static定義內(nèi)部類 1.1 static定義內(nèi)部類 如果說現(xiàn)在內(nèi)部類上使用了static定義,那么這個內(nèi)部類就變成了“外部類”,s
    的頭像 發(fā)表于 10-10 16:08 ?557次閱讀

    如何在普通類中使用內(nèi)部結(jié)構(gòu)

    class Test { public static void main ( String [] args ) { Outer outer = new Outer(); outer.fun(); }} 創(chuàng)建外部類的實例調(diào)用外部類的方法卻執(zhí)行了
    的頭像 發(fā)表于 10-10 16:26 ?431次閱讀

    java內(nèi)部類可以用public修飾嗎

    使用任意訪問權(quán)限修飾符,包括public、protected、默認(rèn)和private。 方法內(nèi)部類只能使用默認(rèn)訪問權(quán)限,即不加任何訪問修飾符。 使用public修飾的內(nèi)部類 內(nèi)部類使用public修飾時,可以被
    的頭像 發(fā)表于 11-21 10:23 ?1199次閱讀

    java內(nèi)存溢出的幾種原因和解決辦法

    內(nèi)存,但是如果程序中存在內(nèi)存泄漏(Memory Leak)或者使用不當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)等問題,仍然有可能導(dǎo)致內(nèi)存溢出。下面將詳細(xì)介紹Java
    的頭像 發(fā)表于 11-23 14:44 ?6185次閱讀