1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        聊聊Java中引用類(lèi)型-引用類(lèi)型應(yīng)用與內(nèi)存泄漏

        共 1344字,需瀏覽 3分鐘

         ·

        2022-01-04 22:00

        本文將接著上一篇文章內(nèi)容,聊聊Java中引用使用以及可能產(chǎn)生的內(nèi)存泄漏。

        Java程序員是幸福的,不用過(guò)多考慮內(nèi)存申請(qǐng)和釋放,Jvm在Java與C++之間構(gòu)建一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的高墻,是的Java程序員能全身心投入到實(shí)際開(kāi)發(fā)當(dāng)中,是否會(huì)有墻外面人想進(jìn)去,墻里面的人卻想出來(lái)呢?

        內(nèi)存溢出和內(nèi)存泄漏:

        • 內(nèi)存溢出:俗稱(chēng)OOM,指JVM無(wú)法申請(qǐng)到足夠內(nèi)存空間或者GC失敗,而拋出的Error,OOM造成的后果十分嚴(yán)重,使得應(yīng)用無(wú)法對(duì)外提供服務(wù)。

        • 內(nèi)存泄漏:部分內(nèi)存已經(jīng)沒(méi)有用了,但是卻沒(méi)有被回收,對(duì)于Java而言,就是GC無(wú)法回收這部分本應(yīng)該被回收的內(nèi)存。

        Obeject 的 finalize

        Object 的finalize方法,當(dāng)對(duì)象即將被回收時(shí),可以被執(zhí)行finalize方法,但是并不能依賴(lài)這個(gè)方法來(lái)清除,否則將造成內(nèi)存泄漏。例如當(dāng)進(jìn)行socket編程時(shí),需要在finally塊中執(zhí)行close方法,從而釋放資源,如果忘記釋放了,則可能會(huì)造成內(nèi)存泄漏,例如在 重寫(xiě)了finalize方法:

        1. /**

        2. * Cleans up if the user forgets to close it.

        3. */

        4. protectedvoid finalize()throwsIOException{

        5. close();

        6. }

        方法注釋也很簡(jiǎn)潔明了,所以在釋放這一類(lèi)工具類(lèi)時(shí),一定要手動(dòng)執(zhí)行close方法,而不是交給finalize去替我們釋放,這樣容易引發(fā)內(nèi)存泄漏。

        ThreadLocal

        ThreadLocal很常見(jiàn),原理不難理解,在Thread類(lèi)中有兩個(gè)ThreadLocalMap變量,維護(hù)著多個(gè)本地線程變量:Thread:

        1. /* ThreadLocal values pertaining to this thread. This map is maintained

        2. * by the ThreadLocal class. */

        3. ThreadLocal.ThreadLocalMap threadLocals =null;


        4. /*

        5. * InheritableThreadLocal values pertaining to this thread. This map is

        6. * maintained by the InheritableThreadLocal class.

        7. */

        8. ThreadLocal.ThreadLocalMap inheritableThreadLocals =null;

        ThreadLocalMap結(jié)構(gòu):

        1. privatestaticfinalint INITIAL_CAPACITY =16;

        2. privateEntry[] table;// 存放元素的數(shù)組

        3. privateint size =0;// 大小

        4. privateint threshold;// Default to 0

        其Entry為一個(gè)WeakReference子類(lèi),reference為對(duì)應(yīng)的ThreadLocal 實(shí)例,即如果沒(méi)有其他引用,就會(huì)被回收:

        1. staticclassEntryextendsWeakReference<ThreadLocal>{

        2. /** The value associated with this ThreadLocal. */

        3. Object value;


        4. Entry(ThreadLocal k,Object v){

        5. super(k);

        6. value = v;

        7. }

        8. }

        Entry 由于沒(méi)有使用引用隊(duì)列,故一旦沒(méi)有引用,則會(huì)直接變?yōu)閕nactive狀態(tài),從而被gc回收。但是另一方面,ThreadLocalMap 中 Entry[] 為一個(gè)引用,所以事實(shí)上,就算ThreadLocal沒(méi)有其他地方使用,也會(huì)被Thread引用,所以只要Thread不銷(xiāo)毀,Entry 并不會(huì)因WeakReference特性而銷(xiāo)毀。另一方面,由于Entry 中key(ThreadLocal)是弱引用類(lèi)型,所以一旦ThreadLocal沒(méi)有被引用,那么ThreadLocal將會(huì)在下次gc被回收,造成的效果為:

        1. Entry 不為null。

        2. Entry實(shí)例的get方法獲取為null,因?yàn)門(mén)hreadLocal已經(jīng)被回收了,但是value仍然存在,這就造成了泄漏。

        在ThreadLocalMap的set操作過(guò)程,雖然在檢查到上述第二種情況,并且會(huì)嘗試將數(shù)組內(nèi)所有滿(mǎn)足該情況的元素節(jié)點(diǎn)的value都設(shè)置為null。這也是為啥追求極致性能的netty會(huì)自己造一個(gè)FastThreadLocal來(lái)取代TheadLocal:https://blog.csdn.net/anLA_/article/details/110777608所以在使用ThreadLocal時(shí),在用完時(shí),一定要執(zhí)行remove方法,清除對(duì)應(yīng)引用。

        Netty中內(nèi)存泄漏檢測(cè)

        netty中通過(guò)BufAllocator使用ByteBuf時(shí),都會(huì)包裝一層校驗(yàn)內(nèi)存泄漏的邏輯:

        1. protectedstaticByteBuf toLeakAwareBuffer(ByteBuf buf){

        2. ResourceLeakTracker<ByteBuf> leak;

        3. switch(ResourceLeakDetector.getLevel()){

        4. case SIMPLE:

        5. leak =AbstractByteBuf.leakDetector.track(buf);

        6. if(leak !=null){

        7. buf =newSimpleLeakAwareByteBuf(buf, leak);

        8. }

        9. break;

        10. case ADVANCED:

        11. case PARANOID:

        12. leak =AbstractByteBuf.leakDetector.track(buf);

        13. if(leak !=null){

        14. buf =newAdvancedLeakAwareByteBuf(buf, leak);

        15. }

        16. break;

        17. default:

        18. break;

        19. }

        20. return buf;

        21. }

        上述會(huì)將ByteBuf封裝一層 DefaultResourceLeak,而 DefaultResourceLeak 則是一個(gè)WeakReference對(duì)象:

        1. DefaultResourceLeak(

        2. Object referent,

        3. ReferenceQueue<Object> refQueue,

        4. Set<DefaultResourceLeak> allLeaks){

        5. super(referent, refQueue);


        6. assert referent !=null;


        7. // Store the hash of the tracked object to later assert it in the close(...) method.

        8. // It's important that we not store a reference to the referent as this would disallow it from

        9. // be collected via the WeakReference.

        10. trackedHash =System.identityHashCode(referent);

        11. allLeaks.add(this);

        12. // Create a new Record so we always have the creation stacktrace included.

        13. headUpdater.set(this,newRecord(Record.BOTTOM));

        14. this.allLeaks = allLeaks;

        15. }

        在應(yīng)用中,對(duì)返回的ByteBuf對(duì)象進(jìn)行操作時(shí),都會(huì)間接調(diào)用 ResourceLeakDetectorreportLeak 方法:

        1. privatevoid reportLeak(){

        2. if(!logger.isErrorEnabled()){

        3. clearRefQueue();

        4. return;

        5. }

        6. // Detect and report previous leaks.

        7. for(;;){

        8. @SuppressWarnings("unchecked")

        9. DefaultResourceLeakref=(DefaultResourceLeak) refQueue.poll();// 如果有弱引用被回收,則會(huì)進(jìn)入隊(duì)列

        10. if(ref==null){

        11. break;

        12. }

        13. if(!ref.dispose()){// 如果已經(jīng)釋放,則不是泄漏

        14. continue;

        15. }

        16. String records =ref.toString();

        17. if(reportedLeaks.putIfAbsent(records,Boolean.TRUE)==null){

        18. if(records.isEmpty()){

        19. reportUntracedLeak(resourceType);

        20. }else{

        21. reportTracedLeak(resourceType, records);

        22. }

        23. }

        24. }

        25. }

        上述方法有以下要點(diǎn):

        1. 如果進(jìn)入refQueue,則說(shuō)明有弱引用被回收,如果dispose了,則說(shuō)明回收之前執(zhí)行release,釋放了資源,否則沒(méi)有釋放。

        2. 報(bào)告泄漏最近調(diào)用路徑

        使用MAT進(jìn)行堆dump分析

        Java引用類(lèi)型,還有一個(gè)用途點(diǎn),就是在使用eclipse mat工具時(shí),由于Java對(duì)象通過(guò)引用鏈接,所以當(dāng)找到一個(gè)大對(duì)象時(shí),可以過(guò)濾其他引用類(lèi)型,直接選擇強(qiáng)引用,從而一步一步最終拿到當(dāng)時(shí)調(diào)用鏈路棧:b949017815da1a944f762d15f6ac3d19.webp這樣就能輕而易舉解決多數(shù)OOM問(wèn)題的根源。

        總結(jié)

        1. Java引用類(lèi)型很強(qiáng)大,可以來(lái)實(shí)現(xiàn)一些高校的應(yīng)用內(nèi)緩存,可以監(jiān)聽(tīng)gc動(dòng)作等。

        2. 根據(jù)部分類(lèi)文檔及結(jié)合代碼來(lái)確定 使用時(shí)要注意是否需要手動(dòng)釋放。

        覺(jué)得對(duì)你有幫助?不如關(guān)注博主公眾號(hào): 六點(diǎn)A君


        瀏覽 79
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            91精品久久久久久久久中文字幕 | 草逼逼美女 | 舌头伸进去的我好爽app 裸体电影院 | 靠逼视频大全 | 成年人免费看视频 | 爱爱视频天天干 | 女人脱了裤衩让男人桶 | 亚洲小视频在线 | 欧美特黄一级片 | 国产精品一二三区夜夜躁 |