GC:GC 是JVM的垃圾回收器。與C/C++不一樣,java程序員無需考慮太多內存分配的位置,更不用考慮內存釋放的機制,java對象內存的申請和釋放都有JVM託管。JVM的內存釋放機制就是GC。
GC的過程分爲獲取內存釋放時機、遍歷無用java對象、釋放算法如何選擇並調度、GC的種類、JVM內存佈局。
首先介紹下JVM的內存佈局,在JVM中,內存分爲虛擬機棧、堆區、方法區、本地方法棧、程序計數器。
程序計數器是被線程似有的,指向當前線程所執行到的字節碼。
一、虛擬機棧是JVM保存待執行的java方法的數據區,全部待執行的java方法會以棧幀的數據形式出棧和入棧,這一過程也表現了java方法的執行過程。
二、本地方法棧是保存的是本地方法?(本地方法如c/c++應該和JVM在操做系統上處於同一層次,爲啥能被JVM操做)本地方法棧爲本地方法服務,在執行一個本地方法時,虛擬機棧會被保存狀態等待本地方法返回(該虛擬機棧中的方法不會被執行引擎加載執行,而是等待方法的返回)(一、本地方法棧相似於對虛擬內存棧區的映射二、本地方法棧中壓入的是本地方法的接口聲明,經過執行引擎加載本地方法庫來獲取本地方法的實現。三、傾向於後者)。
三、方法區保存類型信息。每個類的class信息包括類名、父類、接口,和靜態變量、類方法。
虛擬機棧、程序計數器、本地方法棧都是線程所私有,方法區被線程共享,而堆區是JVM的。
四、堆區存儲的是java對象、包括class對象。有JVM託管,申請和釋放。堆區是GC的主戰場。
-------------------------------------------------------------------
堆被劃分爲兩大區域,young和old(老年代和新年代)。young被分爲Eden,servivor1/2。新生對象內存的申請主要是在Eden和其中一塊servivor中,另外一塊servivor做爲保留,在複製-釋放時,做爲存活對象從新排列的容器。GC分爲Major GC、 Full GC、 Minor GC。
一、GC獲取內存釋放時機:通常發生在new時即JVM分配堆區內存的時候。當Eden區滿的時候觸發Minor GC,當old滿時觸發Full GC
GC的策略有複製-釋放、標記-釋放兩種。複製釋放就是遍歷獲取無用的java對象,將仍然存活的對象複製到另外一塊內存中,再將第一塊內存中的全部對象所有釋放。標記-釋放就是先遍歷獲取無用的java對象,標註標記,在第二次遍歷時將標記的對象釋放。複製釋放的優勢是存活對象將得到從新排列,下降了內存碎片的產生;缺點是必須有另外一塊足夠的內存來容納存活對象,同時複製花費了大量的開銷。標記-釋放的優勢是無需複製的開銷和第二塊內存的開銷,缺點是產生內存利用率下降,內存碎片的數量大大增長甚至致使大塊內存沒法申請。
在JVM採用自適應的方式經過存活內存的多少、在堆區中排列鬆緊判斷採用複製-釋放仍是標記-釋放。當不在使用的對象的數量較少時使用標記-釋放,當對象排列鬆散、內存碎片過量時使用複製-釋放。
二、獲取無用對象:怎樣獲取再也不使用的對象呢,JVM使用從root節點搜索的方式,從root節點遍歷對象鏈表,與root節點不連續的java對象便是不適用的對象,該對象將被釋放。
三、如何調度:上文說道,JVM將採起自適應的方式調度釋放算法,堆內存的劃分就是由此而來。Eden區和其中一塊servivor將做爲內存申請的區域,而另外一塊servivor將用來在複製-釋放時保存從新排列的對象,而後新的內存申請就將在後一塊servivor和Eden中進行,而前一塊用來保存下一次GC存活下來的對象。old域中保存的對象是永久或者長久存在的對象,通過屢次GC後沒有被釋放,就會被移交到old中,由此可知,新生代young中保存的可能是存活週期比較短的,比較久的會在屢次GC沒有釋放後移交到老年代old中。因此Eden的內存被設計得遠遠大於servivor1/2,(在GC後young中仍然存在的內存是比較少的一部分,一個servivor就能保存)。
四、Full GC什麼時候觸發:
永久代無空間申請、新升入永久代的對象內存大於永久代空餘的內存、JVM加載的clas信息佔用內存大於Pen Gen區內存、等。java