在JVM中內存分為兩個部分Stack(棧)和Heap(堆)這裡我們從JVM的內存管理原理的角度來認識Stack和Heap並通過這些原理認清Java中靜態方法和靜態屬性的問題
一般JVM的內存分為兩部分Stack和Heap
Stack(棧)是JVM的內存指令區Stack管理很簡單push一定長度字節的數據或者指令Stack指針壓棧相應的字節位移;pop一定字節長度數據或者指令Stack指針彈棧Stack的速度很快管理很簡單並且每次操作的數據或者指令字節長度是已知的所以Java 基本數據類型Java 指令代碼常量都保存在Stack中
Heap(堆)是JVM的內存數據區Heap 的管理很復雜每次分配不定長的內存空間專門用來保存對象的實例在Heap 中分配一定的內存來保存對象實例實際上也只是保存對象實例的屬性值屬性的類型和對象本身的類型標記等並不保存對象的方法(方法是指令保存在Stack中)在Heap 中分配一定的內存保存對象實例和對象的序列化比較類似而對象實例在Heap 中分配好以後需要在Stack中保存一個字節的Heap 內存地址用來定位該對象實例在Heap 中的位置便於找到該對象實例
由於Stack的內存管理是順序分配的而且定長不存在內存回收問題;而Heap 則是隨機分配內存不定長度存在內存分配和回收的問題;因此在JVM中另有一個GC進程定期掃描Heap 它根據Stack中保存的字節對象地址掃描Heap 定位Heap 中這些對象進行一些優化(例如合並空閒內存塊什麼的)並且假設Heap 中沒有掃描到的區域都是空閒的統統refresh(實際上是把Stack中丟失了對象地址的無用對象清除了)這就是垃圾收集的過程;關於垃圾收集的更深入講解請參考CTO之前的文章《JVM內存模型及垃圾收集策略解析》
JVM的體系結構
我們首先要搞清楚的是什麼是數據以及什麼是指令然後要搞清楚對象的方法和對象的屬性分別保存在哪裡
)方法本身是指令的操作碼部分保存在Stack中;
)方法內部變量作為指令的操作數部分跟在指令的操作碼之後保存在Stack中(實際上是簡單類型保存在Stack中對象類型在Stack中保存地址在Heap 中保存值);上述的指令操作碼和指令操作數構成了完整的Java 指令
)對象實例包括其屬性值作為數據保存在數據區Heap 中
非靜態的對象屬性作為對象實例的一部分保存在Heap 中而對象實例必須通過Stack中保存的地址指針才能訪問到因此能否訪問到對象實例以及它的非靜態屬性值完全取決於能否獲得對象實例在Stack中的地址指針
非靜態方法和靜態方法的區別
非靜態方法有一個和靜態方法很重大的不同非靜態方法有一個隱含的傳入參數該參數是JVM給它的和我們怎麼寫代碼無關這個隱含的參數就是對象實例在Stack中的地址指針因此非靜態方法(在Stack中的指令代碼)總是可以找到自己的專用數據(在Heap 中的對象屬性值)當然非靜態方法也必須獲得該隱含參數因此非靜態方法在調用前必須先new一個對象實例獲得Stack中的地址指針否則JVM將無法將隱含參數傳給非靜態方法
靜態方法無此隱含參數因此也不需要new對象只要class文件被ClassLoader load進入JVM的Stack該靜態方法即可被調用當然此時靜態方法是存取不到Heap 中的對象屬性的
總結一下該過程當一個class文件被ClassLoader load進入JVM後方法指令保存在Stack中此時Heap 區沒有數據然後程序技術器開始執行指令如果是靜態方法直接依次執行指令代碼當然此時指令代碼是不能訪問Heap 數據區的;如果是非靜態方法由於隱含參數沒有值會報錯因此在非靜態方法執行前要先new對象在Heap 中分配數據並把Stack中的地址指針交給非靜態方法這樣程序技術器依次執行指令而指令代碼此時能夠訪問到Heap 數據區了
靜態屬性和動態屬性
前面提到對象實例以及動態屬性都是保存在Heap 中的而Heap 必須通過Stack中的地址指針才能夠被指令(類的方法)訪問到因此可以推斷出靜態屬性是保存在Stack中的而不同於動態屬性保存在Heap 中正因為都是在Stack中而Stack中指令和數據都是定長的因此很容易算出偏移量也因此不管什麼指令(類的方法)都可以訪問到類的靜態屬性也正因為靜態屬性被保存在Stack中所以具有了全局屬性
在JVM中靜態屬性保存在Stack指令內存區動態屬性保存在Heap數據內存區
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26460.html