內存泄漏的原因及解決


轉自:https://my.oschina.net/hiease/blog/1613871

雖然jvm有垃圾回收機制,如果程序編寫不注意某些特定規則,仍然會導致java程序內存泄漏,最終可能出現OutOfMemory異常。

1.Java內存泄漏的原因

java中的對象從使用上分為2種類型,被引用(referenced)的和不被引用(unreferenced)的。垃圾回收只會回收不被引用的對象。被引用的對象,即使已經不再使用了,也不會被回收。因此如果程序中有大量的被引用的無用對象時,就是出現內存泄漏。

2.java堆內存(Heap)泄漏

jvm堆內存的大小是通過 -Xms 和 -Xmx兩個參數指定的。

2.1 對象被靜態成員引用

當大對象被靜態成員引用時,會造成內存泄漏。

示例:

private Random random = new Random();

public static final ArrayList<Double> list = new ArrayList<Double>(1000000);

for (int i = 0; i < 1000000; i++) { list.add(random.nextDouble()); }

ArrayList是在堆上動態分配的對象,正常情況下使用完畢后,會被gc回收,但是在此示例中,由於被靜態成員list引用,而靜態成員是不會被回收的,所以會導致這個很大的ArrayList一直停留在堆內存中。

因此需要特別注意靜態成員的使用方式,避免靜態成員引用大對象或集合類型的對象(如ArrayList等)。

2.2 String的intern方法

在大字符串上調用String.intern() 方法,intern()會將String放在jvm的內存池中(PermGen ),而jvm的內存池是不會被gc的。因此如果大字符串調用intern()方法后,會產生大量的無法gc的內存,導致內存泄漏。

如果必須要使用大字符串的intern方法,應該通過-XX:MaxPermSize參數調整PermGen內存的大小。

2.3 讀取流后沒有關閉

開發中經常忘記關閉流,這樣會導致內存泄漏。因為每個流在操作系統層面都對應了打開的文件句柄,流沒有關閉,會導致操作系統的文件句柄一直處於打開狀態,而jvm會消耗內存來跟蹤操作系統打開的文件句柄。 示例:

BufferedReader br = new BufferedReader(new FileReader(path));
return br.readLine();

要解決這個問題,在java8之前的版本中可以在finally中加入關閉操作:

 BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }

java8中可以使用try-with-resources語句:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

對於網絡連接和數據庫連接等也要注意連接的關閉,如果采用了連接池,那關閉操作是由連接池負責的,程序中可以不用處理。

2.4 將沒有實現hashCode()和equals()方法的對象加入到HashSet中

這是一個簡單卻很常見的場景。正常情況下Set會過濾重復的對象,但是如果沒有hashCode() 和 equals()實現,重復對象會不斷被加入到Set中,並且再也沒有機會去移除。

因此給類都加上hashCode() 和 equals()方法的實現是一個好的編程習慣。可以通過Lombok的@EqualsAndHashCode很方便實現這種功能。

3. 查找內存泄漏的方法

3.1 記錄gc日志

通過在jvm參數中指定-verbose:gc,可以記錄每次gc的詳細情況,用於分析內存的使用。

3.2 進行profiling

通過Visual VM或jdk自帶的Java Mission Control,進行內存分析。

3.3 代碼審查

通過代碼審查和靜態代碼檢查,發現導致內存泄漏問題的錯誤代碼。

4. 總結

代碼層面的檢查可以幫助發現部分內存泄漏的問題,但是生產環境中的內存泄漏往往不容易提前發現,因為很多問題是在大並發場景下才會出現。因此還需要通過壓力測試工具進行壓力測試,提前發現潛在的內存泄漏問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM