最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java內(nèi)存泄露的示例代碼

 更新時間:2017年12月08日 08:42:21   投稿:mrr  
這篇文章通過一個Demo來簡要介紹下ThreadLocal和ClassLoader導(dǎo)致內(nèi)存泄露最終OutOfMemory的場景。下面通過示例代碼給大家分享Java內(nèi)存泄露的相關(guān)知識,感興趣的朋友一起看看吧

在定位JVM性能問題時可能會遇到內(nèi)存泄露導(dǎo)致JVM OutOfMemory的情況,在使用Tomcat容器時如果設(shè)置了reloadable=”true”這個參數(shù),在頻繁熱部署應(yīng)用時也有可能會遇到內(nèi)存溢出的情況。Tomcat的熱部署原理是檢測到WEB-INF/classes或者WEB-INF/lib目錄下的文件發(fā)生了變更后會把應(yīng)用先停止然后再啟動,由于Tomcat默認給每個應(yīng)用分配一個WebAppClassLoader,熱替換的原理就是創(chuàng)建一個新的ClassLoader來加載類,由于JVM中一個類的唯一性由它的class文件和它的類加載器來決定,因此重新加載類可以達到熱替換的目的。當(dāng)熱部署的次數(shù)比較多會導(dǎo)致JVM加載的類比較多,如果之前的類由于某種原因(比如內(nèi)存泄露)沒有及時卸載就可能導(dǎo)致永久代或者MetaSpace的OutOfMemory。這篇文章通過一個Demo來簡要介紹下ThreadLocal和ClassLoader導(dǎo)致內(nèi)存泄露最終OutOfMemory的場景。

類的卸載

在類使用完之后,滿足下面的情形,會被卸載:

1.該類在堆中的所有實例都已被回收,即在堆中不存在該類的實例對象。

2.加載該類的classLoader已經(jīng)被回收。

3.該類對應(yīng)的Class對象沒有任何地方可以被引用,通過反射訪問不到該Class對象。

如果類滿足卸載條件,JVM就在GC的時候,對類進行卸載,即在方法區(qū)清除類的信息。

場景介紹

上一篇文章我介紹了ThreadLocal的原理,每個線程有個ThreadLocalMap,如果線程的生命周期比較長可能會導(dǎo)致ThreadLocalMap里的Entry沒法被回收,那ThreadLocal的那個對象就一直被線程持有強引用,由于實例對象會持有Class對象的引用,Class對象又會持有加載它的ClassLoader的引用,這樣就會導(dǎo)致Class無法被卸載了,當(dāng)加載的類足夠多時就可能出現(xiàn)永久代或者MetaSpace的內(nèi)存溢出,如果該類有大對象,比如有比較大的字節(jié)數(shù)組,會導(dǎo)致Java堆區(qū)的內(nèi)存溢出。

源碼介紹

這里定義了一個內(nèi)部類Inner,Inner類有個靜態(tài)的ThreadLocal對象,主要用于讓線程持有Inner類的強引用導(dǎo)致Inner類無法被回收,定義了一個自定義的類加載器去加載Inner類,如下所示:

public class MemoryLeak {
  public static void main(String[] args) {
 //由于線程一直在運行,因此ThreadLocalMap里的Inner對象一直被Thread對象強引用
    new Thread(new Runnable() {
      @Override
      public void run() {
        while (true) {
   //每次都新建一個ClassLoader實例去加載Inner類
          CustomClassLoader classLoader = new CustomClassLoader
              ("load1", MemoryLeak.class.getClassLoader(), "com.ezlippi.MemoryLeak$Inner", "com.ezlippi.MemoryLeak$Inner$1");
          try {
            Class<?> innerClass = classLoader.loadClass("com.ezlippi.MemoryLeak$Inner");
            innerClass.newInstance();
   //幫助GC進行引用處理
            innerClass = null;
            classLoader = null;
            Thread.sleep(10);
          } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
 //為了更快達到堆區(qū)
  public static class Inner {
    private byte[] MB = new byte[1024 * 1024];
    static ThreadLocal<Inner> threadLocal = new ThreadLocal<Inner>() {
      @Override
      protected Inner initialValue() {
        return new Inner();
      }
    };
 //調(diào)用ThreadLocal.get()才會調(diào)用initialValue()初始化一個Inner對象
    static {
      threadLocal.get();
    }
    public Inner() {
    }
  }
 //源碼省略
  private static class CustomClassLoader extends ClassLoader {}

堆區(qū)內(nèi)存溢出

為了觸發(fā)堆區(qū)內(nèi)存溢出,我在Inner類里面設(shè)置了一個1MB的字節(jié)數(shù)組,同時要在靜態(tài)塊中調(diào)用threadLocal.get(),只有調(diào)用才會觸發(fā)initialValue()來初始化一個Inner對象,不然只是創(chuàng)建了一個空的ThreadLocal對象,ThreadLocalMap里并沒有數(shù)據(jù)。

JVM參數(shù)如下:

-Xms100m -Xmx100m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintClassHistogram -XX:+HeapDumpOnOutOfMemoryError

最后執(zhí)行了814次后JVM堆區(qū)內(nèi)存溢出了,如下所示:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid11824.hprof ...
Heap dump file created [100661202 bytes in 1.501 secs]
Heap
 par new generation  total 30720K, used 30389K [0x00000000f9c00000, 0x00000000fbd50000, 0x00000000fbd50000)
 eden space 27328K, 99% used [0x00000000f9c00000, 0x00000000fb6ad450, 0x00000000fb6b0000)
 from space 3392K, 90% used [0x00000000fb6b0000, 0x00000000fb9b0030, 0x00000000fba00000)
 to  space 3392K,  0% used [0x00000000fba00000, 0x00000000fba00000, 0x00000000fbd50000)
 concurrent mark-sweep generation total 68288K, used 67600K [0x00000000fbd50000, 0x0000000100000000, 0x0000000100000000)
 Metaspace    used 3770K, capacity 5134K, committed 5248K, reserved 1056768K
 class space  used 474K, capacity 578K, committed 640K, reserved 1048576K
Exception in thread "Thread-0" java.lang.OutOfMemoryError: Java heap space
 at com.ezlippi.MemoryLeak$Inner.<clinit>(MemoryLeak.java:34)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at java.lang.Class.newInstance(Unknown Source)
 at com.ezlippi.MemoryLeak$1.run(MemoryLeak.java:20)
 at java.lang.Thread.run(Unknown Source)

可以看到JVM已經(jīng)沒有內(nèi)存來創(chuàng)建新的Inner對象,因為堆區(qū)存放了很多個1MB的字節(jié)數(shù)組,這里我把類的直方圖打印出來了(下圖是堆大小為1024M的場景),省略了一些無關(guān)緊要的類,可以看出字節(jié)數(shù)組占了855M的空間,創(chuàng)建了814個 com.ezlippi.MemoryLeak$CustomClassLoader 的實例,和字節(jié)數(shù)組的大小基本吻合:

 num   #instances     #bytes class name
----------------------------------------------
  1:     6203   855158648 [B
  2:     13527    1487984 [C
  3:      298     700560 [I
  4:     2247     228792 java.lang.Class
  5:     8232     197568 java.lang.String
  6:     3095     150024 [Ljava.lang.Object;
  7:     1649     134480 [Ljava.util.HashMap$Node;
 11:      813     65040 com.ezlippi.MemoryLeak$CustomClassLoader
 12:      820     53088 [Ljava.util.Hashtable$Entry;
 15:      817     39216 java.util.Hashtable
 16:      915     36600 java.lang.ref.SoftReference
 17:      543     34752 java.net.URL
 18:      697     33456 java.nio.HeapCharBuffer
 19:      817     32680 java.security.ProtectionDomain
 20:      785     31400 java.util.TreeMap$Entry
 21:      928     29696 java.util.Hashtable$Entry
 22:     1802     28832 java.util.HashSet
 23:      817     26144 java.security.CodeSource
 24:      814     26048 java.lang.ThreadLocal$ThreadLocalMap$Entry

Metaspace溢出

為了讓Metaspace溢出,那就必須把MetaSpace的空間調(diào)小一點,要在堆溢出之前加載足夠多的類,因此我調(diào)整了下JVM參數(shù),并且把字節(jié)數(shù)組的大小調(diào)成了1KB,如下所示:

private byte[] KB = new byte[1024];
-Xms100m -Xmx100m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintClassHistogram -XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=2m

從 GC日志可以看出在Meraspace達到GC閾值(也就是MaxMetaspaceSize配置的大小時)會觸發(fā)一次FullGC:

java.lang.OutOfMemoryError: Metaspace
 <<no stack trace available>>
{Heap before GC invocations=20 (full 20):
 par new generation  total 30720K, used 0K [0x00000000f9c00000, 0x00000000fbd50000, 0x00000000fbd50000)
 eden space 27328K,  0% used [0x00000000f9c00000, 0x00000000f9c00000, 0x00000000fb6b0000)
 from space 3392K,  0% used [0x00000000fb6b0000, 0x00000000fb6b0000, 0x00000000fba00000)
 to  space 3392K,  0% used [0x00000000fba00000, 0x00000000fba00000, 0x00000000fbd50000)
 concurrent mark-sweep generation total 68288K, used 432K [0x00000000fbd50000, 0x0000000100000000, 0x0000000100000000)
 Metaspace    used 1806K, capacity 1988K, committed 2048K, reserved 1056768K
 class space  used 202K, capacity 384K, committed 384K, reserved 1048576K
[Full GC (Metadata GC Threshold) [CMS
Process finished with exit code 1

通過上面例子可以看出如果類加載器和ThreadLocal使用的不當(dāng)確實會導(dǎo)致內(nèi)存泄露的問題,完整的源碼在github

相關(guān)文章

  • 一篇文章解決Java異常處理

    一篇文章解決Java異常處理

    這篇文章主要給大家介紹了關(guān)于如何通過一篇文章解決Java異常處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Spring Boot + Vue 基于 RSA 的用戶身份認證加密機制實現(xiàn)過程

    Spring Boot + Vue 基于 RSA 的用戶身份

    RSA是一種非對稱加密算法,適用于用戶身份認證加密,本文介紹了基于RSA的用戶身份認證加密機制的實現(xiàn),包括前端Vue.js使用jsencrypt庫對用戶名密碼進行加密,后端使用RSA私鑰解密驗證用戶憑據(jù),感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Apache Commons Math3探索之快速傅立葉變換代碼示例

    Apache Commons Math3探索之快速傅立葉變換代碼示例

    這篇文章主要介紹了Apache Commons Math3探索之快速傅立葉變換代碼示例,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • JAVA日期處理類詳解

    JAVA日期處理類詳解

    這篇文章主要介紹了Java實現(xiàn)的日期處理類,結(jié)合完整實例形式分析了Java針對日期的獲取、運算、轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下
    2021-08-08
  • Java String 和StringBuffer的詳解及區(qū)別

    Java String 和StringBuffer的詳解及區(qū)別

    這篇文章主要介紹了Java String 和StringBuffer的詳解及區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java中的注解與注解處理器

    Java中的注解與注解處理器

    這篇文章主要介紹了Java中的注解與注解處理器,元注解的作用是負責(zé)注解其他注解, Java5.0定義了4個標準的meta-annotation(元注解)類型,它們被用來提供對其它注解類型進行說明,需要的朋友可以參考下
    2023-11-11
  • SpringBoot中四種常用的條件裝配技術(shù)詳解

    SpringBoot中四種常用的條件裝配技術(shù)詳解

    Spring?Boot提供了多種條件裝配技術(shù),允許開發(fā)者根據(jù)不同條件動態(tài)配置應(yīng)用程序,本文將介紹Spring?Boot中四種常用的條件裝配技術(shù),需要的可以參考下
    2025-04-04
  • 九個動畫組圖輪播總結(jié)全棧數(shù)據(jù)結(jié)構(gòu)數(shù)組鏈表

    九個動畫組圖輪播總結(jié)全棧數(shù)據(jù)結(jié)構(gòu)數(shù)組鏈表

    數(shù)據(jù)結(jié)構(gòu)和算法是密不可分的,兩者往往是相輔相成的存在,所以在學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)過程中,不免會遇到各種算法,數(shù)據(jù)結(jié)構(gòu)常用操作一般為:增刪改查?;旧纤械臄?shù)據(jù)結(jié)構(gòu)都是圍繞這幾個操作進行展開,本文用九張動圖來闡述先進后出的數(shù)據(jù)結(jié)構(gòu)
    2021-08-08
  • 不規(guī)范使用ThreadLocal導(dǎo)致bug分析解決

    不規(guī)范使用ThreadLocal導(dǎo)致bug分析解決

    這篇文章主要為大家介紹了不規(guī)范使用ThreadLocal導(dǎo)致bug分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-01-01
  • Java 將Excel轉(zhuǎn)為SVG的方法

    Java 將Excel轉(zhuǎn)為SVG的方法

    本文以Java示例展示如何將Excel文檔轉(zhuǎn)為SVG格式。通過本文中的方法,在將Excel轉(zhuǎn)為SVG時,如果sheet工作表中手動設(shè)置了分頁,則將每個分頁的內(nèi)容單獨保存為一個svg文件,如果sheet工作表中沒有設(shè)置分頁,則將Excel sheet表格中默認的分頁范圍保存為svg。
    2021-05-05

最新評論

彭阳县| 萝北县| 曲麻莱县| 密山市| 安仁县| 平谷区| 夏津县| 麻江县| 江孜县| 遂溪县| 长寿区| 迭部县| 冷水江市| 合作市| 青州市| 宁国市| 庆安县| 明水县| 汤原县| 左云县| 汽车| 高清| 绥德县| 遂宁市| 嘉善县| 普陀区| 乡宁县| 洛浦县| 项城市| 新蔡县| 古浪县| 九寨沟县| 甘泉县| 佳木斯市| 东城区| 连云港市| 商南县| 京山县| 察雅县| 锡林浩特市| 东乌珠穆沁旗|