自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

面試官:小伙子,你給我說一下Java中什么情況會(huì)導(dǎo)致內(nèi)存泄漏呢?

開發(fā) 后端
指程序中動(dòng)態(tài)分配內(nèi)存給一些臨時(shí)對(duì)象,但對(duì)象不會(huì)被GC回收,它始終占用內(nèi)存,被分配的對(duì)象可達(dá)但已無用。即無用對(duì)象持續(xù)占有內(nèi)存或無用對(duì)象的內(nèi)存得不到及時(shí)釋放,從而造成的內(nèi)存空間浪費(fèi)。

概念

內(nèi)存泄露:指程序中動(dòng)態(tài)分配內(nèi)存給一些臨時(shí)對(duì)象,但對(duì)象不會(huì)被GC回收,它始終占用內(nèi)存,被分配的對(duì)象可達(dá)但已無用。即無用對(duì)象持續(xù)占有內(nèi)存或無用對(duì)象的內(nèi)存得不到及時(shí)釋放,從而造成的內(nèi)存空間浪費(fèi)。

可達(dá)性分析算法

JVM使用可達(dá)性分析算法判斷對(duì)象是否存活。

GC Root

通過一系列名為“GC Roots”的對(duì)象作為起點(diǎn),從這些結(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為“引用鏈(Reference Chain)”,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何飲用鏈相連時(shí),則證明此對(duì)象是不可用的。

object4、object5、object6雖然有互相判斷,但是它們到GC Rootd是不可達(dá)的,所以它們將會(huì)判定為是可回收對(duì)象。

可以作為GC Roots的對(duì)象有:

  •  虛擬機(jī)棧(棧幀中的本地變量表)中的引用的對(duì)象;
  •  方法區(qū)中的類靜態(tài)屬性引用的對(duì)象;
  •  方法區(qū)中的常量引用的對(duì)象;
  •  本地方法棧中JNI的引用的對(duì)象

雖然Java有垃圾收集器幫組實(shí)現(xiàn)內(nèi)存自動(dòng)管理,雖然GC有效的處理了大部分內(nèi)存,但是并不能完全保證內(nèi)存的不泄漏。

內(nèi)存泄漏

內(nèi)存泄漏就是堆內(nèi)存中不再使用的對(duì)象無法被垃圾收集器清除掉,因此它們會(huì)不必要地存在。這樣就導(dǎo)致了內(nèi)存消耗,降低了系統(tǒng)的性能,最終導(dǎo)致OOM使得進(jìn)程終止。

內(nèi)存泄漏的表現(xiàn):

  •  應(yīng)用程序長(zhǎng)時(shí)間連續(xù)運(yùn)行時(shí)性能嚴(yán)重下降;
  •  應(yīng)用程序中的OutOfMemoryError堆錯(cuò)誤;
  •  自發(fā)且奇怪的應(yīng)用程序崩潰;
  •  應(yīng)用程序偶爾會(huì)耗盡連接對(duì)象;

可能導(dǎo)致內(nèi)存泄漏的原因:

1. static字段引起的內(nèi)存泄漏

大量使用static字段會(huì)潛在的導(dǎo)致內(nèi)存泄漏,在Java中,靜態(tài)字段通常擁有與整個(gè)應(yīng)用程序相匹配的生命周期。

解決辦法:最大限度的減少靜態(tài)變量的使用;單例模式時(shí),依賴于延遲加載對(duì)象而不是立即加載的方式(即采用懶漢模式,而不是餓漢模式)

2. 未關(guān)閉的資源導(dǎo)致內(nèi)存泄漏

每當(dāng)創(chuàng)建連接或者打開流時(shí),JVM都會(huì)為這些資源分配內(nèi)存。如果沒有關(guān)閉連接,會(huì)導(dǎo)致持續(xù)占有內(nèi)存。在任意情況下,資源留下的開放連接都會(huì)消耗內(nèi)存,如果不處理,就會(huì)降低性能,甚至OOM。

解決辦法:使用finally塊關(guān)閉資源;關(guān)閉資源的代碼,不應(yīng)該有異常;JDK1.7之后,可以使用太try-with-resource塊。

3. 不正確的equals()和hashCode()

在HashMap和HashSet這種集合中,常常用到equal()和hashCode()來比較對(duì)象,如果重寫不合理,將會(huì)成為潛在的內(nèi)存泄漏問題。

解決辦法:用最佳的方式重寫equals()和hashCode().

4. 引用了外部類的內(nèi)部類

非靜態(tài)內(nèi)部類的初始化,總是需要外部類的實(shí)例;默認(rèn)情況下,每個(gè)非靜態(tài)內(nèi)部類都包含對(duì)其外部類的隱式引用,如果我們?cè)趹?yīng)用程序中使用這個(gè)內(nèi)部類對(duì)象,那么即使在我們的外部類對(duì)象超出范圍后,它也不會(huì)被垃圾收集器清除掉。

解決辦法:如果內(nèi)部類不需要訪問外部類包含的類成員,可以轉(zhuǎn)換為靜態(tài)類。

5. finalize方法導(dǎo)致的內(nèi)存泄漏

重寫finalize()方法時(shí),該類的對(duì)象不會(huì)立即被垃圾收集器收集,如果finalize()方法的代碼有問題,那么會(huì)潛在的印發(fā)OOM;

解決辦法:避免重寫finalize()方法。

6. 常量字符串造成的內(nèi)存泄漏

如果我們讀取一個(gè)很大的String對(duì)象,并調(diào)用了intern(),那么它將放到字符串池中,位于PermGen中,只要應(yīng)用程序運(yùn)行,該字符串就會(huì)保留,這就會(huì)占用內(nèi)存,可能造成OOM。(針對(duì)JDK1.6及以前,常量池在PermGen永久代中)

解決辦法:增加PermGen的大小,-XX:MaxPermSize=512M;JDK1.7以后字符串池轉(zhuǎn)移到了堆中。

intern()方法詳解: 

  1. String str1 = "abc" 
  2. String str2 = "abc" 
  3. String str3 = new String("abc");  
  4. String str4 = str3.intern();  
  5. System.out.println(str1 == str2);  
  6. System.out.println(str2 == str3);  
  7. System.out.println(str1 == str4);  
  8. System.out.println(str3 == str4);  
  9. true, false, true, false 

intern()方法搜索字符串常量池,如果存在指定的字符串,就返回之;

否則,就將該字符串放入常量池并返回之。

換言之,intern()方法保證每次返回的都是 同一個(gè)字符串對(duì)象 

  1. String str1 = "abc" 
  2. String str2 = "abc" 
  3. String str3 = new String("abcd");  
  4. String str4 = str3.intern();  
  5. String str5 = "abcd" 
  6. System.out.println(str1 == str2);  
  7. System.out.println(str2 == str3);  
  8. System.out.println(str1 == str4);  
  9. System.out.println(str3 == str4);  
  10. System.out.println(str4 == str5);  
  11. true  
  12. false  
  13. false  
  14. false  
  15. true 

為何要使用intern()方法?看看equals方法的源碼: 

  1. public boolean equals(Object anObject) {  
  2.     if (this == anObject) {  
  3.         return true;  
  4.     } 
  5.      if (anObject instanceof String) {  
  6.         String anotherString = (String)anObject; 
  7.          int n = value.length;  
  8.         if (n == anotherString.value.length) {  
  9.             char v1[] = value;  
  10.             char v2[] = anotherString.value;  
  11.             int i = 0 
  12.             while (n-- != 0) {  
  13.                 if (v1[i] != v2[i])  
  14.                     return false;  
  15.                 i++;  
  16.             }  
  17.             return true;  
  18.         }  
  19.     }  
  20.     return false;  

可以看到,比較兩個(gè)字符串的時(shí)候,首先比較兩個(gè)字符串對(duì)象是否地址相同,不同再挨個(gè)比較字符。這樣就大大加快了比較的速度。否則若每次都挨個(gè)比較將是非常耗時(shí)的。

7. 使用ThreadLocal造成內(nèi)存泄漏

使用ThreadLocal時(shí),每個(gè)線程只要處于存活狀態(tài)就可保留對(duì)其ThreadLocal變量副本的隱式調(diào)用,且將保留其自己的副本。使用不當(dāng),就會(huì)引起內(nèi)存泄漏。

一旦線程不再存在,該線程的threadLocal對(duì)象就應(yīng)該被垃圾收集,而現(xiàn)在線程的創(chuàng)建都是使用線程池,線程池有線程重用的功能,因此線程就不會(huì)被垃圾回收器回收。所以使用到ThreadLocal來保留線程池中的線程的變量副本時(shí),ThreadLocal沒有顯式地刪除時(shí),就會(huì)一直保留在內(nèi)存中,不會(huì)被垃圾回收。

解決辦法:不再使用ThreadLocal時(shí),調(diào)用remove()方法,該方法刪除了此變量的當(dāng)前線程值。不要使用ThreadLocal.set(null),它只是查找與當(dāng)前線程關(guān)聯(lián)的Map并將鍵值中這個(gè)threadLocal對(duì)象所對(duì)應(yīng)的值為null,并沒有清除這個(gè)鍵值對(duì)。

最后

每天都會(huì)分享java相關(guān)技術(shù)文章或行業(yè)資訊,歡迎大家關(guān)注和轉(zhuǎn)發(fā)文章! 

 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2022-06-07 12:03:33

Java內(nèi)存模型

2025-03-10 07:05:07

2023-09-12 14:56:13

MyBatis緩存機(jī)制

2020-07-30 07:58:36

加密算法

2021-08-13 07:23:15

架構(gòu)秒殺系統(tǒng)

2021-07-28 10:08:19

類加載代碼塊面試

2023-11-29 08:00:53

JavaTreeMap底層

2022-06-06 15:33:20

線程Java釋放鎖

2023-02-08 08:32:41

輪詢鎖

2023-02-18 13:34:14

Nacos健康檢查機(jī)制

2022-09-14 19:50:22

事務(wù)場(chǎng)景流程

2022-06-27 07:23:44

MySQL常量優(yōu)化

2021-06-02 11:25:18

線程池Java代碼

2024-02-21 16:42:00

2024-02-27 15:23:48

RedLock算法Redis

2023-12-29 13:45:00

2024-01-29 10:08:11

零拷貝Zero-copyCPU 拷貝

2024-11-20 08:00:00

死鎖多線程編程

2023-01-30 15:39:40

GETHTTP

2021-11-27 08:13:13

Final 面試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)