java虛擬機(jī)有一套自動內(nèi)存管理機(jī)制浩淘,不需要為每一個(gè)new對象去寫相應(yīng)的delete方法芳肌,不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出的問題瘦棋,但是在開發(fā)過程中如果不了解java虛擬機(jī)的內(nèi)存管理的話一旦出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出也很難進(jìn)行排查被廓。
一党觅、java內(nèi)存區(qū)域與內(nèi)存溢出
java虛擬機(jī)在執(zhí)行java程序的過程中把它所管理的內(nèi)存分為若干個(gè)不同的數(shù)據(jù)區(qū)域
1.1术荤、程序計(jì)數(shù)器
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間倚喂,可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。
線程私有的內(nèi)存瓣戚。
由于java虛擬機(jī)中多線程是通過輪流切換并分配處理器執(zhí)行時(shí)間來實(shí)現(xiàn)的端圈,在任何一個(gè)確定的時(shí)刻,一個(gè)處理器(多核處理器一個(gè)內(nèi)核)都只會執(zhí)行一個(gè)線程中的指令子库。因此舱权,為了線程切換后能恢復(fù)到正確的位置,每個(gè)線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器
1.2仑嗅、java虛擬機(jī)棧
?? java虛擬機(jī)棧描述的是java方法執(zhí)行的內(nèi)存模型:每個(gè)方法在執(zhí)行的同時(shí)都會創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲局部變量宴倍、操作棧數(shù)张症、動態(tài)鏈接、方法出口等信息啊楚。(方法執(zhí)行到完成對應(yīng)入棧到出棧)
??虛擬機(jī)棧中的局部變表存放基礎(chǔ)數(shù)據(jù)類型和對象的引用吠冤。
??與程序計(jì)數(shù)器一樣,java虛擬機(jī)棧也是線程私有的恭理,他的生命周期與線程相同拯辙。
??如果線程請求的棧深度大于虛擬機(jī)所允許的棧深度,將拋出StackOverflowError異逞占郏⊙谋#現(xiàn)在java虛擬機(jī)一般都是可擴(kuò)展的,如果擴(kuò)展時(shí)無法申請到足夠的內(nèi)存周伦,會拋出OutOfMemoryError夕春。
1.3、本地方法棧
1.4专挪、java堆
??java堆是java虛擬機(jī)管理的內(nèi)存中最大的一塊及志。java堆是所有線程共享的一塊區(qū)域,在虛擬機(jī)啟動時(shí)創(chuàng)建寨腔,該區(qū)域的作用是存放對象實(shí)例速侈,幾乎所有的對象實(shí)例都在這里創(chuàng)建。
??java堆是垃圾收集器管理的主要區(qū)域迫卢,由于現(xiàn)在收集器基本都是采用分代回收算法倚搬,所以java堆中還可以細(xì)分為新生代和老年代。
??如果在堆中沒有內(nèi)存完成實(shí)例分配乾蛤,并且堆也無法在擴(kuò)展每界,將會拋出OOM。
1.5家卖、方法區(qū)
??各個(gè)線程共享眨层,用于存儲已被虛擬機(jī)加載的類信息,常量上荡,靜態(tài)變量以及及時(shí)編譯器編譯后的代碼等數(shù)據(jù)谐岁。方法區(qū)還有個(gè)別名叫"非堆"。
二榛臼、垃圾回收
提到垃圾回收伊佃,一般說來,我們要解決一些三個(gè)問題:
- 哪些內(nèi)存回收沛善?
- 什么時(shí)候回收航揉?
- 如何回收?
這些問題分別對應(yīng)著引用管理和回收策略等方案金刁。
2.1帅涂、判斷對象是否死亡
- 引用計(jì)數(shù)算法
有對這個(gè)對象的引用就+1议薪,不再引用就-1,任何時(shí)刻計(jì)數(shù)為0的對象就代表不再使用媳友。
實(shí)現(xiàn)簡單斯议、效率高、但存在循環(huán)引用的問題 - 可達(dá)性分析算法
可達(dá)性分析算法通過一系列稱為GC Roots的對象作為起始點(diǎn)醇锚,從這些節(jié)點(diǎn)從上向下搜索哼御,搜索走過的路徑稱為引用鏈,當(dāng)一個(gè)對象沒有任何引用鏈 與GC Roots連接時(shí)就說明此對象不可用焊唬,也就是對象不可達(dá)恋昼。
GC Roots對象通常包括:
- 虛擬機(jī)棧中引用的對象(棧幀中的本地變量表)
- 方法區(qū)中類的靜態(tài)屬性引用的對象
- 方法區(qū)中常量引用的對象
- Native方法引用的對象
可達(dá)性分析算法整個(gè)流程如下所示:
??第一次標(biāo)記:對象在經(jīng)過可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots有引用鏈,則進(jìn)行第一次標(biāo)記并進(jìn)行一次篩選赶促,篩選條件是:該對象是否有必要執(zhí)行finalize()方法液肌。沒有覆蓋finalize()方法或者finalize()方法已經(jīng)被執(zhí)行過都會被 認(rèn)為沒有必要執(zhí)行。
如果有必要執(zhí)行:則該對象會被放在一個(gè)F-Queue隊(duì)列鸥滨,并稍后在由虛擬機(jī)建立的低優(yōu)先級Finalizer線程中觸發(fā)該對象的finalize()方法嗦哆,但不保證一定等待它執(zhí)行結(jié)束,因?yàn)槿绻@個(gè)對象的finalize()方法發(fā)生了死循環(huán)或者執(zhí)行 時(shí)間較長的情況婿滓,會阻塞F-Queue隊(duì)列里的其他對象老速,影響GC。
??第二次標(biāo)記:GC對F-Queue隊(duì)列里的對象進(jìn)行第二次標(biāo)記空幻,如果在第二次標(biāo)記時(shí)該對象又成功被引用,則會被移除即將回收的集合容客,否則會被回收秕铛。
2.2、引用類型
- 強(qiáng)引用:代碼中普遍存在的缩挑,只要強(qiáng)引用還存在但两,垃圾收集器就不會回收掉被引用的對象。
- 軟引用:SoftReference供置,用來描述還有用但是非必須的對象谨湘,當(dāng)內(nèi)存不足的時(shí)候回回收這類對象。
- 弱引用:WeakReference芥丧,用來描述非必須對象紧阔,弱引用的對象只能生存到下一次GC發(fā)生時(shí),當(dāng)GC發(fā)生時(shí)续担,無論內(nèi)存是否足夠擅耽,都會回收該對象。
- 虛引用:PhantomReference物遇,一個(gè)對象是否有虛引用的存在乖仇,完全不會對其生存時(shí)間產(chǎn)生影響憾儒,也無法通過虛引用取得一個(gè)對象的引用,它存在的唯一目的是在這個(gè)對象被回收時(shí)可以收到一個(gè)系統(tǒng)通知乃沙。