JVM又曾放過誰,垃圾終將被回收!

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"背景","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Java中有一個很重要的概念,即","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"一切皆對象","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。所謂對象,就是將現實中的事物抽象出來,進而可以通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"繼承","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"實現","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"組合","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的方式把萬事萬物都給容納,所以理解對象的概念在學習Java(包括所有的面向對象的語言)的過程中至關重要。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們在程序中需要使用某個對象的時候,它就是爺爺,即使採用反射的方法也得把它創建出來;當我們不需要它的時候,它就是個垃圾,即使它能逃過新生代,在老年代也要依然追殺你。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天,我們要學習的是JVM如何處理","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾(對象)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",在學習之前,我們先思考以下幾個問題:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象的生存週期有那些階段?","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如何判斷一個對象已經成爲了垃圾?","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"垃圾如何標記?","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"如何回收垃圾(對象),有哪些策略?","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"1、對象的生存週期","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常情況下,作爲一名Java開發者,我們可以肆無忌憚的new對象,而不需要管理這些對象的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"生存週期,","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"因爲JVM會幫我們打掃衛生(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"管理對象","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":")。但是,作爲進階的Coder,我們還是要認真理解一下的。下面我們先了解下的對象的生存週期。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"1.1宏觀視角","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從宏觀的角度看,對象的生存週期可以是:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象創建","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"--->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象的使用-","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"-->","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象的回收","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象創建","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象創建可以採用new指令、反序列化、反射等方法,這一步驟主要是爲對象分配內存並初始化。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象的使用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象的使用是將JVM棧中的對象的引用去定位、訪問堆中的對象的具體位置,常採用的方法有句柄和直接指針。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象的回收","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象的回收就是垃圾回收,我們在下文中詳解","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"僅僅看這三大塊還是有點不明所以,太粗糙,下面我們以微觀的角度分析對象的生存週期。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"1.2微觀視角","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從微觀視角來看,對象的生存的週期可以大致分爲七個階段:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"創建階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"應用階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"不可見階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"不可達階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可收集階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"終結階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象空間重分配階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/93/93f3235fe71443f06aecb6d5fd4f75ba.png","alt":"image-20210204162022401","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"創建階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象創建階段主要是爲對象分配內存空間、開始構造對象並完成對static成員的初始化。對象一旦被創建,並且分派給某些變量賦值,這個對象的狀態就被切換到了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"應用階段","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"attrs":{}},{"type":"text","marks":[{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"應用階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"應用階段就是對象至少被一個強引用關聯着。(莫慌,強引用的概念在下文中講解)","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不可見階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"當一個對象處於不可見階段時,說明程序不再持有該對象的任何強引用,但是這些引用可能還存在着,一般情況下是指程序的執行已經超過該作用域了。","attrs":{}}]}],"attrs":{}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"boolean flag= false;\nif(flag){\n flag = 0;\n num++;\n }\nSystem.out.println(num);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的程序中,本地變量num在System.out.println(num)時已經超出了其作用域,程序就認爲變量num處於不可見階段。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不可達階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象處於不可達階段是指該對象不再被任何強引用所持有,但是該對象仍可能被JVM等系統下的某些已裝載的靜態變量或線程或JNI等強引用持有着,這些特殊的強引用被稱爲”GC root”。存在着這些GC root會導致對象的內存泄露情況,無法被回收.","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"可收集階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"當垃圾回收器發現該對象已經處於“不可達階段”而且垃圾回收器已經對該對象的內存空間又一次分配做好準備時,則對象進入了“收集階段”。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"終結階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"當對象運行完finalize方法後仍然處於不可達狀態時,則該對象進入終結階段。在該階段是等待垃圾回收器對該對象空間進行回收。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"對象空間重分配階段","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"對象空間又一次分配階段,垃圾回收器對該對象的所佔用的內存空間進行回收或者再分配了,則該對象徹底消失了,稱之爲“對象空間又一次分配階段”。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面落落索索一大堆,看的小夥伴似懂非懂,而小夥伴只想知道什麼時候","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象變垃圾","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"以及","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾(對象)被清除","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上,這些階段伴隨着對","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"象的創建","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象的使用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象失效","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象被標記爲垃圾","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象被回收","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的整個流程。爲了滿足你們的要求,我們把問題聚焦到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾(對象)","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的問題上,不過,在聚焦問題之前,我們最先理解下","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"對象引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的概念,因爲這對於對象變垃圾非常有幫助!","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"1.3對象引用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從JDK1.2開始,Java的設計人員將對象的引用分爲四類,分別是:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"強引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"軟引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"弱引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"虛引用","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"強引用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"強引用表示一個對象處在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"【有用,必須】","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的狀態,它是使用最普遍的引用。如果一個對象具有強引用,那麼垃圾回收器絕不會回收它。就算在內存空間不足的情況下,Java虛擬機寧可拋出OutOfMemoryError錯誤,使程序異常終止,也不會通過回收具有強引用的對象來解決內存不足的問題。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Student student = new Student(); // 這就是強引用","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"軟引用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟引用表示一個對象處在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"【有用,但非必須】","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的狀態。在內存空間足夠的情況下,如果一個對象只具有軟引用,那麼垃圾回收器就不會回收它,但是如果內存空間不足,垃圾回收器就會回收這個對象(回收發生在OutOfMemoryError錯誤之前)。只要垃圾回收器沒有回收它,這個對象就能被程序使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟引用用來實現內存敏感的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"高速緩存","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",比如說:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"網頁緩存","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"圖片緩存","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"等。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Student student = new Student();\nSoftReference softReference = new SoftReference(student);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"弱引用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"弱引用表示一個對象處在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"【可能有用,但非必須】","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的狀態。類似於軟引用,但是強度比軟引用更弱一些:只具有弱引用的對象擁有更短暫的生命週期。GC線程在掃描它所管轄的內存區域的過程中,一旦發現只具有弱引用的對象,就會回收掉這些被弱引用關聯的對象。也就是說,無論當前內存是否緊缺,GC都會回收被弱引用關聯的對象。不過,由於GC是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經典實用案列","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.langThreadLocal","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"中","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9d/9d1be1e7842bfdc7b5953bc6536a89ae.png","alt":"image-20210204175914106","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"虛引用","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虛引用表示一個對象處在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"【無用】","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的狀態,意味着虛引用等同於沒有引用,在任何時候都可能被GC回收。設置虛引用的目的是爲了被虛引用關聯的對象在被垃圾回收器回收的時候,能夠收到一個系統通知(用來跟蹤對象被GC回收的活動)。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ReferenceQueue referenceQueue = new ReferenceQueue();\nPhantomReference phantomReference = new PhantomReference(object,queue);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2、垃圾(對象)判斷","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"垃圾回收的第一步就是要判斷對象是否是垃圾。事實上,一個對象是否是垃圾不是由我們程序員來判斷的,更準確的說是我們Java Developer不需要的關心的,如果你真的是有代碼潔癖或者強迫症,你調用gc方法也無可厚非。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上,垃圾回收主要有兩中算法:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"引用計數算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"可達性分析算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.1引用計數算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引用計數算法是一種很古老的算法,現在在很多java版本已經廢棄掉了,作爲一名學習者,還有是有必要了解一下的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"定義","attrs":{}},{"type":"text","text":":引用計數算法是在對象中添加了一個引用計數器,當引用這個對象時,計數就加1;當引用失效的時候,計數器就減1,當引用計數數爲0的時候,該對象也就失效了,變成了垃圾,JVM也就開始回收它了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從定義上來看,引用計數算法還是很容易理解的,它是一個優缺點很明顯的一種算法,我們接着往下看。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"優點","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"①引用計數算法原理簡單,並且實時性較強,當引用計數器爲0的時候,JVM就可以直接對它進行回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"②引用計數器只作用於單個對象,即JVM掃描時,只會掃描該對象,不會順着引用掃描全部對象。","attrs":{}}]}],"attrs":{}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"缺點","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"①對象每次被引用時,都需要耗費一定的時間去更新引用計數器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#6a737d","name":"user"}},{"type":"bgcolor","attrs":{"color":"#fff9f9","name":"user"}}],"text":"②會出現引用循環問題,即對象A引用對象B,對象B引用對象A,由於A和B相互引用,計數它們不被需要了,它們也不會被JVM當做垃圾回收。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了解決引用計數算法產生的問題,優秀的Java開發者提出了另一種算法:可達性分析算法。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"2.2可達性分析算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可達性分析算法的思路是通過一系列的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"“GC Roots”","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"對象作爲起點進行搜索,如果在“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,則認爲該對象可以被回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可達性分析算法如下圖所示:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/47/474ff82b6da559ada356286d6e83180a.png","alt":"image-20210204215715942","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與引用計數算法的不同的是,引用計數算法判斷的是對象是否死亡,而可達性分析算法分析的對象是否還活着,可達性分析算法可以有效解決引用計數算法中的循環引用問題。可達性分析算法是判斷對象是否爲垃圾的主流算法。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"3、垃圾標記","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上文中我們已經講解了如何判斷一個對象是否是垃圾,及採用的是引用計數法和可達性分析法。小夥伴肯定發現這兩種算法是即判斷對象是否爲垃圾,同時標記了對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"是的,更準確的說:如果一個對象不存在引用,那麼這個對象就是垃圾。而引用計數算法中的計數器和可達性分析算法中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"引用鏈","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"都是標記對象的方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們也在上面說過,由於引用計數算法存在循環引用問題,所以現有的主流標記算法是可達性分析算法。下面,我們將詳細分析下可達性分析算法標記垃圾對象的過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Java中,可以作爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"GC Roots","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的對象通常包含以下幾種:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"虛擬機棧中引用的對象","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"方法區中靜態屬性引用的對象","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"方法區中常量引用的對象","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"本地方法中(Native)引用的對象","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在可達性分析算法中,即使存在不可達的對象,該對象也不一定是非死不可的,一個對象要真正的死亡,要經歷兩次標記的過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"標記過程分析:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當使用可達性分析算法分析對象時,若發現一些對象與GC Root鏈不可達,那麼該對象就會被","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"第一次標記","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",然後進行篩選,篩選的條件是判斷該對象有沒有必要執行finalize()方法(此方法每個對象默認都有),但如果對象沒有重寫finalize()方法或者對象的finalize方法已經被虛擬機調用過一次了,則都將視爲“沒有必要執行”,垃圾回收器可以直接回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果該對象被判定爲有必要執行finalize()方法,那麼虛擬機會把這個對象放置在一個的隊列中,然後由一個專門的Finalizer線程去執行這個對象的finalize()方法。如果此時存在某些對象重新與引用鏈上的任何一個對象建立了關聯,那麼在第二次標記時它將被移這個隊列。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"4、垃圾回收","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內存是很寶貴的資源,對於不需要的垃圾對象,我們要儘管把它驅逐出去。上文講解的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾(對象)判斷","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾標記","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"步驟都是爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾回收","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"做鋪墊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在文章","attrs":{}},{"type":"link","attrs":{"href":"https://mp.weixin.qq.com/s/kDnHV0D6aA-tUjK-fVco5w","title":null},"content":[{"type":"text","text":"JVM內存","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#036aca","name":"user"}}]},{"type":"text","text":"中,我們詳細分析JVM的運行時數據區,不熟悉的同學可以再去學習下。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"4.1堆的佈局結構分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上,垃圾回收的地方是運行時數據區的堆中,我們都知道對象的存活是有周期的,如果一個對象沒有被引用,那麼就可以認爲該對象可以被清除掉了,就是我們認爲的垃圾。由於每個對象存活的時間不同,爲了減少GC線程掃描垃圾時間及頻率,我們可以將存活時間較長的對象單獨放一個區域。因此,堆的佈局也就確定下來了。總的來說,堆被劃分成兩部分:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"新生代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"老年代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/73/7396cce2f3802cc3c4219f03bfaf521d.png","alt":"image-20210205113109427","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"image-20210205113109427","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新生代和老年代比值爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"1:2","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",這個比例並不是唯一的,我們可以可以通過參數 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"–XX:NewRatio","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"按照具體的場景來指定,如果再細粒度的劃分,新生代又可以分爲E","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"den區","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Survivor區","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",而","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Survivor區","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"又可以分爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"FromSurvivor","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ToSurvivor","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",默認比值爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"8:1:1","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/51/51135753338165a848fbd8dc2a2b82ca.png","alt":"image-20210205113247700","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這時問題又來了,爲什麼要將Survivor分爲兩塊相等大小的空間啊?好問題,我先說答案,這兩分爲兩部分主要是爲了解決","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"內存碎片化","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的問題,如果內存碎片化嚴重,也就是兩個對象佔用不連續的內存,已有的連續內存不夠新對象存放,就會觸發垃圾回收(GC)。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#135ce0","name":"user"}}],"text":"4.2垃圾回收算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上文中我們分析了運行時數據區中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"堆的佈局結構","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",按照對象的生存週期將堆分成了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"新生代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"老年代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",新生代又細分爲了三個區:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Eden","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"From Survivor","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"To Surviver","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":",比例是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"8:1:1","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。理解","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"堆","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"的佈局結構對理解JVM中的垃圾回收算法的流程非常有幫助。爲什麼這麼說呢?因爲垃圾對象主要是在堆中,又因爲堆切分了不同的分區,根據每塊分區特性採用的垃圾回收算法也是不同的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在下面的學習中,我們先學習下常用的垃圾回收算法,最後根據堆中不同的分區內存的特性選擇合適的垃圾回收算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常情況下,常用的垃圾回收算法有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"標記-清除算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"標記複製算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"標記整理算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"分代回收算法","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"標記-清除算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"標記清除算法分爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"標記","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"清除","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"兩個階段,即首先標記出需要回收的對象,標記完成後統一清理對象。它的優點是效率高,缺點是容易產生內存碎片。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"標記階段是以根節點(GC Roots)爲起點在引用鏈上進行掃描,對存活的對象進行標記。清除階段是掃描整個對象集合,清除集合中未被標記的對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"垃圾標記","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"垃圾回收","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"階段如下圖所示:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f4/f4c718ba88ec9b653abd25fa7fb9f9b6.png","alt":"image-20210205162605167","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"標記-複製算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"標記複製算法是將內存劃分兩個空間,在任意的時間點,所有動態分配的對象都只能分配在其中一個空間,這個空間可以被稱爲活動空間,而另外一個空間則是空閒的。當有效內存空間耗盡時,JVM將暫停程序運行,開啓複製算法GC線程。接下來GC線程會將活動區間內的存活對象,全部複製到空閒區間,且嚴格按照內存地址依次排列,與此同時,GC線程將更新存活對象的內存引用地址指向新的內存地址。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​ 此時,空閒區間已經與活動區間交換,而垃圾對象現在已經全部留在了原來的活動區間,也就是現在的空閒區間。事實上,在活動區間轉換爲空間區間的同時,垃圾對象已經被一次性全部回收。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ab/ab1f07e29f1e2bb42e9a080921e8c6da.png","alt":"image-20210205175603999","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"標記整理算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"標記整理算法與標記清除算法很相似,也是分爲兩個階段:標記和整理,。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"標記","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":":它的第一個階段與標記/清除算法是一模一樣的,均是遍歷GC Roots,然後將存活的對象標記。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"整理","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":":移動所有存活的對象,且按照內存地址次序依次排列,然後將末端內存地址以後的內存全部回收。因此,第二階段才稱爲整理階段","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ce/cef2662d3234e0eee1f960e66296b823.png","alt":"image-20210205195350250","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"分代回收算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目前大多數JVM垃圾收集器採用的算法都是分代回收算法,其思想是根據對象存活的生命週期將內存劃分成三個區域:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"新生代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"老年代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"永久代","attrs":{}}],"marks":[{"type":"color","attrs":{"color":"#1394d8","name":"user"}}],"attrs":{}},{"type":"text","text":"。劃分區域可以參考堆的內存結構,新生代和老年代和堆中的劃分是一一對應,而永久代在Java8中已經用元空間代替了。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"新生代回收算法","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新生代主要存放生命週期較短的對象,所有新生成的對象首先都應該放在新生代中的Eden區,回收時將Eden區存活對象複製到To Survivor區,然後清空eden區。當To Survivor區也存放滿了時,則將Eden區和To Survivor區存活對象複製到From Survivor區,然後清空Eden和這個From Survivor區,此時To Survivor區爲空,然後將To Survivor區和From Survivor區交換,即保持From Survivor區爲空, 如此往復。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當From Survivor區不足以存放 Eden區和To Survivor區的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發一次Full GC,也就是新生代、老年代都進行回收。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般情況下,新生代發生的GC也叫做Minor GC,MinorGC發生頻率比較高(不一定等Eden區滿了才觸發)。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"老年代回收算法","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在新生代中經歷了N次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認爲年老代中存放的都是一些生命週期較長的對象。老年代的內存比新生代也大很多(大概比例是1:2),當老年代內存滿時觸發Major GC即Full GC,Full GC發生頻率比較低,老年代對象存活時間比較長,存活率標記高。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"永久代回收算法","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"永久代用於存放靜態文件,如Java類、方法等。永久代對垃圾回收沒有顯著影響,但是有些應用可能動態生成或者調用一些class,例如Hibernate 等,在這種時候需要設置一個比較大的永久代空間來存放這些運行過程中新增的類。在JDK1.8之前,永久代稱方法區,在JDK1.8後,永久代被稱爲元空間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"堆結構中的不同分區中的算法選擇","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上,JVM對堆中的不同區域(新生代和老年代)採用不同的算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新生代比較適合複製算法,新生代有Eden、From Survivor和To Survivor三個區,因爲Eden區中對象會被複制到To Survivor,且From Survivor和To Survivor相互交換比較頻繁,所以採用複製算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"老年代中的對象的生命週期比較長,所以不適合複製算法,在老年代一般採用的是標記-整理/清除算法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"參考文獻","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[1]https://www.sohu.com/a/359103068_675634","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[2]https://www.cnblogs.com/widget90/p/12932172.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[3]https://baijiahao.baidu.com/s?id=1663281048126158605&wfr=spider&for=pc","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[4]https://www.cnblogs.com/jichi/p/11139437.html","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"[5]深入理解JVM虛擬機值(第三版).周志華","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章