內存泄露:指程序中動態分配內存給一些臨時對象,但對象不會被GC回收,它始終佔用內存,被分配的對象可達但已無用。即無用對象持續佔有內存或無用對象的內存得不到及時釋放,從而形成的內存空間浪費。java
JVM使用可達性分析算法判斷對象是否存活。算法
經過一系列名爲「GC Roots」的對象做爲起點,從這些結點開始向下搜索,搜索所走過的路徑稱爲「引用鏈(Reference Chain)」,當一個對象到GC Roots沒有任何飲用鏈相連時,則證實此對象是不可用的。性能
object四、object五、object6雖然有互相判斷,可是它們到GC Rootd是不可達的,因此它們將會斷定爲是可回收對象。this
能夠做爲GC Roots的對象有:spa
雖然Java有垃圾收集器幫組實現內存自動管理,雖然GC有效的處理了大部份內存,可是並不能徹底保證內存的不泄漏。線程
內存泄漏就是堆內存中再也不使用的對象沒法被垃圾收集器清除掉,所以它們會沒必要要地存在。這樣就致使了內存消耗,下降了系統的性能,最終致使OOM使得進程終止。code
內存泄漏的表現:對象
大量使用static字段會潛在的致使內存泄漏,在Java中,靜態字段一般擁有與整個應用程序相匹配的生命週期。blog
解決辦法:最大限度的減小靜態變量的使用;單例模式時,依賴於延遲加載對象而不是當即加載的方式(即採用懶漢模式,而不是餓漢模式)生命週期
每當建立鏈接或者打開流時,JVM都會爲這些資源分配內存。若是沒有關閉鏈接,會致使持續佔有內存。在任意狀況下,資源留下的開放鏈接都會消耗內存,若是不處理,就會下降性能,甚至OOM。
解決辦法:使用finally塊關閉資源;關閉資源的代碼,不該該有異常;JDK1.7以後,可使用太try-with-resource塊。
在HashMap和HashSet這種集合中,經常用到equal()和hashCode()來比較對象,若是重寫不合理,將會成爲潛在的內存泄漏問題。
解決辦法:用最佳的方式重寫equals()和hashCode().
非靜態內部類的初始化,老是須要外部類的實例;默認狀況下,每一個非靜態內部類都包含對其外部類的隱式引用,若是咱們在應用程序中使用這個內部類對象,那麼即便在咱們的外部類對象超出範圍後,它也不會被垃圾收集器清除掉。
解決辦法:若是內部類不須要訪問外部類包含的類成員,能夠轉換爲靜態類。
重寫finalize()方法時,該類的對象不會當即被垃圾收集器收集,若是finalize()方法的代碼有問題,那麼會潛在的印發OOM;
解決辦法:避免重寫finalize()方法。
若是咱們讀取一個很大的String對象,並調用了intern(),那麼它將放到字符串池中,位於PermGen中,只要應用程序運行,該字符串就會保留,這就會佔用內存,可能形成OOM。(針對JDK1.6及之前,常量池在PermGen永久代中)
解決辦法:增長PermGen的大小,-XX:MaxPermSize=512M;JDK1.7之後字符串池轉移到了堆中。
intern()方法詳解:
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = str3.intern(); System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str4); System.out.println(str3 == str4); true, false, true, false
intern()方法搜索字符串常量池,若是存在指定的字符串,就返回之;
不然,就將該字符串放入常量池並返回之。
換言之,intern()方法保證每次返回的都是 同一個字符串對象
String str1 = "abc"; String str2 = "abc"; String str3 = new String("abcd"); String str4 = str3.intern(); String str5 = "abcd"; System.out.println(str1 == str2); System.out.println(str2 == str3); System.out.println(str1 == str4); System.out.println(str3 == str4); System.out.println(str4 == str5); true false false false true
爲什麼要使用intern()方法?看看equals方法的源碼:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
能夠看到,比較兩個字符串的時候,首先比較兩個字符串對象是否地址相同,不一樣再挨個比較字符。這樣就大大加快了比較的速度。不然若每次都挨個比較將是很是耗時的。
使用ThreadLocal時,每一個線程只要處於存活狀態就可保留對其ThreadLocal變量副本的隱式調用,且將保留其本身的副本。使用不當,就會引發內存泄漏。
一旦線程再也不存在,該線程的threadLocal對象就應該被垃圾收集,而如今線程的建立都是使用線程池,線程池有線程重用的功能,所以線程就不會被垃圾回收器回收。因此使用到ThreadLocal來保留線程池中的線程的變量副本時,ThreadLocal沒有顯式地刪除時,就會一直保留在內存中,不會被垃圾回收。
解決辦法:再也不使用ThreadLocal時,調用remove()方法,該方法刪除了此變量的當前線程值。不要使用ThreadLocal.set(null),它只是查找與當前線程關聯的Map並將鍵值中這個threadLocal對象所對應的值爲null,並無清除這個鍵值對。
感謝你看到這裏,看完有什麼的不懂的能夠在評論區問我,以爲文章對你有幫助的話記得給我點個贊,天天都會分享java相關技術文章或行業資訊,歡迎你們關注和轉發文章!