深入java虛擬機第5章, 堆堆java程序在運行時所創(chuàng)建的所有類實例或數(shù)組都存放在同一個堆中。而每個java虛擬機實例中只存在一個堆空間,因此所有線程都將共享這個堆,又由于每個java程序獨占一個java虛擬機實例,因此都有它自己的堆空間。 因此要考慮多線程訪問對象(堆數(shù)據(jù))的同步的問題了。 java虛擬機有一條在堆中分配新對象的指令,但是沒有釋放內存的指令。java虛擬機的垃圾回收機制負責回收沒有被使用的內存。 只要有一個對象引用,虛擬機就必須能夠快速定位對象類型的數(shù)據(jù)。另外它也必須通過該對象引用訪問相應的類數(shù)據(jù)(存儲于方法區(qū)中的類型信息)。因此在對象中通常會有一個指向方法區(qū)的指針。 堆空間的設計舉例一種設計:把堆分為兩部分:一個句柄池,一個對象池。句柄池的每個條目有兩個部分,一個指向對象實例的指針,一個指向方法區(qū)類型數(shù)據(jù)的指針。這種設計的好處是便于堆碎片的整理。每次移動對象池中的對象,只需要更改指向對象的新地址就可以了,缺點是每次訪問對象的實例變量都要經過兩次指針的傳遞。
另一種設計:對象指針直接指向一組數(shù)據(jù),該數(shù)據(jù)包括對象實例數(shù)據(jù)和指向方法區(qū)的指針。但是使得當整理內存碎片而移動對象變得復雜。
java虛擬機使用一種特殊的數(shù)據(jù)結構把方法區(qū)和對象引用聯(lián)系起來。這個特殊的數(shù)據(jù)結構位于方法區(qū):由一個指向方法區(qū)對應類數(shù)據(jù)的指針和此對象的方法表。 方法表是一個指針數(shù)組,其中每一項都是之歌指向“實例方法數(shù)據(jù)”的指針,實例方法可以被那類的對象調用。 實例方法數(shù)據(jù)包含以下信息: 1)此方法的操作數(shù)棧和局部變量區(qū)的大小 2)此方法的字節(jié)碼 3)異常表
每個對象都有一個對象鎖,用來在多個線程訪問這個對象時進行同步。這個鎖是可重入鎖。一個線程可以追加請求并獲得這個鎖,但請求幾次,也要釋放幾次。
每個對象邏輯上還有與實現(xiàn)等待集合(wait set)的數(shù)據(jù)相關聯(lián)。鎖是用來實現(xiàn)多個線程對共享數(shù)據(jù)的互斥訪問的,而等待集合是為了多個線程為了實現(xiàn)同一個共同目標而協(xié)調工作的。
等待集合由等待方法和通知方法聯(lián)合使用,每個類都從objec繼承了三個等待方法,wai()的重載方法和兩個通知方法notify和notifyall。 當一個線程調用等待方法時,java虛擬機就阻塞這個線程,并將這個線程放入這個對象的等待集合,直到另外一個線程在這個對象上調用通知方法,這個線程中的一個或者多個線程才會被喚醒。
方法區(qū)的結構方法表,方法數(shù)據(jù),類中所有數(shù)據(jù)的入口點。
最后一種數(shù)據(jù)是堆中對象的垃圾收集管理數(shù)據(jù)。垃圾收集器必須使用某種方式追蹤程序引用的每個對象。 另外在java中數(shù)組是真正的對象,因此也存放在堆中。
程序計數(shù)器每個java線程都有自己的pc寄存器,當線程啟動時創(chuàng)建。pc寄存器的大小是一個字長,即能維持一個本地指針,也能持有一個returnAddress。當線程執(zhí)行某個方法時,pc寄存器的內容總是下一個被執(zhí)行指令的“地址”,這里的地址可以是一個指針,也可以是該方法起始指令的偏移量。如果該線程在執(zhí)行一個本地方法,這個寄存器的值為undefined。 |
|