<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 1344字,需瀏覽 3分鐘

           ·

          2022-01-04 22:00

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

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

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

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

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

          Obeject 的 finalize

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

          1. /**

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

          3. */

          4. protectedvoid finalize()throwsIOException{

          5. close();

          6. }

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

          ThreadLocal

          ThreadLocal很常見,原理不難理解,在Thread類中有兩個ThreadLocalMap變量,維護著多個本地線程變量: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為一個WeakReference子類,reference為對應(yīng)的ThreadLocal 實例,即如果沒有其他引用,就會被回收:

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

          1. Entry 不為null。

          2. Entry實例的get方法獲取為null,因為ThreadLocal已經(jīng)被回收了,但是value仍然存在,這就造成了泄漏。

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

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

          netty中通過BufAllocator使用ByteBuf時,都會包裝一層校驗內(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. }

          上述會將ByteBuf封裝一層 DefaultResourceLeak,而 DefaultResourceLeak 則是一個WeakReference對象:

          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)用中,對返回的ByteBuf對象進行操作時,都會間接調(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();// 如果有弱引用被回收,則會進入隊列

          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. }

          上述方法有以下要點:

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

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

          使用MAT進行堆dump分析

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

          總結(jié)

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

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

          覺得對你有幫助?不如關(guān)注博主公眾號: 六點A君


          瀏覽 79
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香五月婷婷久久 | 久艹艹| 国产在线视频一区 | 欧美性爱成人视频 | 日本欧美一级色情片免费观看 |