Java的垃圾回收機制

jvm運行時,數據區包含:虛擬機棧,堆,方法區,本地方法棧,程序計數器,其中,堆和方法區是線程共享的,虛擬機棧和程序計數器是線程私有的。 】   


        內存回收就是釋放掉沒有任何引用的對象所佔的內存空間(從root搜索不到,而且經過第一次標記、清理後,仍然沒有復活的對象)。

首先,要判斷怎樣的對象可以被回收?這裏有2種方法:

    1.採用標記計數的方法:

給內存中的對象給打上標記,對象被引用一次,計數就加1,引用被釋放了,計數就減一,當這個計數爲0的時候,這個對象就可以被回收了。當然,這也就引發了一個問題:循環引用的對象是無法被識別出來並且被回收的。所以就有了第二種方法:

    2.採用根搜索算法

從一個根出發,搜索所有的可達對象,這樣剩下的那些對象就是需要被回收的

判斷完了哪些對象是沒用的,這樣就可以進行回收了

最簡單的,就是直接清空那個需要被回收的對象。但是這又出現了一個問題,就是內存會被分爲一塊一塊的小碎片。

爲了解決這個問題,可以採用第二種方法,就是在之前的基礎上將存活的對象給整理一下,使他們變成一個連續的內存,從而釋放出連續的較大的內存空間。

還有一中回收方法就是採用複製的辦法:將內存分爲2塊,一塊用來存放對象,另一塊用來放着,當存放對象的那塊滿了以後就將上面存活的對象給複製過來,然後在這塊內存上工作,並且將之前的內存清空,當自己這塊滿了以後再複製回去,如此反覆。


比較效率的一中做法是將以上的幾種方法給結合起來。

    首先將內存分塊,分爲新生代,老年代和永久代。

    永久代用來存放代碼,等一些基本不改變的數據,

    新生代用來存放剛產生的一些對象,新生代又可分爲3塊。分別爲Edon區,Survivor0,survivor1,剛產生的對象是放在Edon區中,當這個區塊放滿了以後就將其存活的部分複製到survivor0塊中,並且將Edon區中的數據清空,等到survivor0滿了就將其中的存活的數據放到survivor1中,清空survivor0,垃圾回收到了一定次數還未被回收的對象,就可以放到老年區。一般來說,剛纔產生的對象大多是要在下一次垃圾回收的時候就要被回收掉的,只有一小部分對象會被保留下來,這些被保留下來的對象都是比較穩定的,所以在老年區中的對象回收方法可以採用整理的方法,而在Edon區等新生代中採用複製的方法比較好。


萬一被問到還有呢。。想了想,還可以扯些其他方面的:

垃圾回收他是在虛擬機空閒的時候或者內存緊張的時候執行的,什麼時候回收不是由程序員來控制的,這也就是Java比較耗內存的原因之一。

還有在垃圾回收的時候當檢測到對象沒有用了,需要被回收的時候並不會馬上被回收,而是將其放入到一個準備回收的隊列,去執行finalize方法。。然等到下次內存回收的時候要是他還是沒有被任何人引用的話,就將其給回收了。(如果在finalize方法中重新給對象加個引用,這樣對象是有可能不會被回收的)不過finalize方法不推薦使用,他跟C++中的析構函數不同,我們既不能確定什麼時候他回被回收,也不能保證這個方法一定會被執行。



Java虛擬機規範將JVM虛擬機所管理的內存分爲幾部分?a30c88d8d3c43f75cb73ce0fe113a78b_b.png

1.  程序計數器(Program Counter Register)是一塊較小的內存空間,它的作用可以看做是當前線程所執行字節碼的行號指示器。是線程私有,生命週期與線程相同。
2. Java虛擬機棧(Java Virtual Machine Stacks)也是線程私有的,它的生命週期與線程相同。
Java虛擬機棧描述的是Java方法(區別於native的本地方法)執行的內存模型:每個方法被執行的時候都會同時創建一個棧幀(Stack Frame)用於存儲局部變量表、操作棧、動作鏈接、方法出口等信息。每個方法被調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。
 3. 本地方法棧(Native Method Stacks)與虛擬機棧所發揮的作用是非常相似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則爲虛擬機所使用到的Native方法服務。

4. 方法區

(3)有哪些方法可以判斷一個對象已經可以被回收,JVM怎麼判斷一個對象已經消亡可以被回收?
    ①引用計數算法
      給對象中添加一個引用計數器,每當有一個地方引用它時,計數器就加1;當引用失效時,計數器值就減1;任何時刻計數器都爲0的對象就是不可能再被使用的。
      Java語言沒有選用引用計數法來管理內存,因爲引用計數法不能很好的解決循環引用的問題。
   ②根搜索算法
      在主流的商用語言中,都是使用根搜索算法來判定對象是否存活的。
      GC Root Tracing 算法思路就是通過一系列的名爲"GC  Roots"的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連,即從GC Roots到這個對象不可達,則證明此對象是不可用的。cb06b4bd6d62cf310b7f3014ab5cb2fc_b.png


比如上圖,左邊的對象都是存活的,右邊的都是可以回收的。

(4)那些對象可以作爲GC Roots?
   虛擬機棧(棧幀中的本地變量表)中的引用的對象
   方法區中的類靜態屬性引用的對象
   方法區中的常量引用的對象
   本地方法棧中JNI(Native方法)的引用對象

(5)Java代碼編譯的結果是什麼?
    是字節碼文件.class

(6) 怎麼理解Java語言的平臺無關性?語言無關性?通過什麼方法實現的? 

            Java是平臺無關的語言是指用Java寫的應用程序不用修改就可在不同的軟硬件平臺上運行。
            Java主要靠Java虛擬機(JVM)在目標碼級實現平臺無關性。JVM是一種抽象機器,它附着在具體操作系統之上,本身具有一套虛機器指令,並有自己的棧、寄存器組等。但JVM通常是在軟件上而不是在硬件上實現。(目前,SUN系統公司已經設計實現了Java芯片,主要使用在網絡計算機NC上。另外,Java芯片的出現也會使Java更容易嵌入到家用電器中。)JVM是Java平臺無關的基礎,在JVM上,有一個Java解釋器用來解釋Java編譯器編譯後的程序。Java編程人員在編寫完軟件後,通過Java編譯器將Java源程序編譯爲JVM的字節代碼。任何一臺機器只要配備了Java解釋器,就可以運行這個程序,而不管這種字節碼是在何種平臺上生成的(過程如圖1所示)。另外,Java採用的是基於IEEE標準的數據類型。通過JVM保證數據類型的一致性,也確保了Java的平臺無關性。
            實現語言無關性的基礎仍然是虛擬機和字節碼存儲格式。Java虛擬機不和包括Java在內的任何語言綁定,它只與“Class文件”這種特定的二進制文件格式所關聯,Class文件中包含了Java虛擬機指令集和符號表以及若干其他輔助信息。基於安全方面的考慮,Java虛擬機規範要求在Class文件中使用許多強制性的語法和結構化約束,但任一門功能性語言都可以表示 爲一個能被Java虛擬機所接受的有效的Class文件。作爲一個通用的、機器無關的執行平臺, 任何其他語言的實現者都可以將Java虛擬機作爲語言的產品交付媒介。

(7)Java中的static變量和static方法在JVM運行中內存的分配管理有什麼不同和一般變量方法?

          靜態對象                非靜態對象 
擁有屬性: 是類共同擁有的           是類各對象獨立擁有的
內存分配: 內存空間上是固定的        空間在各個附屬類裏面分配 
分配順序: 先分配靜態對象的空間      繼而再對非靜態對象分配空間,也就是初 始化順序是先靜態

(9)在heap中沒有類實例的時候,類信息還存在於JVM嗎? 存在於什麼地方?

        java對象要包含的基本數據至少要有兩部分:1、類以及超類的實例聲明的實例變量;2、指向類數據的引用,jvm需要通過此引用找到該對象的(可能存在的)方法表、類型信息。其中類型信息包括類型基本信息、常量池、字段信息、方法信息、類變量

HotSpot JVM (下簡稱JVM)的內存管理,詳見:http://www.iteye.com/topic/894148


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章