標籤:work href 地址 寫代碼 cto strong 分配 eve article
在JVM中,記憶體分為兩個部分,Stack(棧)和Heap(堆),這裡,我們從JVM的記憶體管理原理的角度來認識Stack和Heap,並通過這些原理認清Java中靜態方法和靜態屬性的問題。
一般,JVM的記憶體分為兩部分:Stack和Heap。(stack和heap都是位於RAM中的)
stack(棧)是JVM的記憶體指令區。stack管理很簡單,push一定長度位元組的資料或者指令,stack指標壓棧相應的位元組位移;pop一定位元組長度資料或者指令,stack指標彈棧。stack的速度很快,管理很簡單,並且每次操作的資料或者指令位元組長度是已知的。
所以Java基礎資料型別 (Elementary Data Type),Java指令代碼,常量都儲存在stack中。
heap (堆)是JVM的記憶體資料區。heap 的管理很複雜,每次分配不定長的記憶體空間,專門用來儲存對象的執行個體。
在heap 中分配一定的記憶體來儲存對象執行個體,實際上也只是儲存對象執行個體的屬性值、屬性的類型和對象本身的類型標記等,並不儲存對象的方法(方法是指令,儲存在stack中),在heap 中分配一定的記憶體儲存對象執行個體和對象的序列化比較類似。
而對象執行個體在heap 中分配好以後,需要在stack中儲存一個4位元組的heap 記憶體位址,用來定位該對象執行個體在heap 中的位置,便於找到該對象執行個體。
由於stack的記憶體管理是順序分配的,而且定長,不存在記憶體回收問題;而heap則是隨機分配記憶體,不定長度,存在記憶體配置和回收的問題;因此在JVM中另有一個GC進程,定期掃描heap ,它根據stack中儲存的4位元組對象地址掃描heap ,定位heap中這些對象,進行一些最佳化(例如合并空閑記憶體塊什麼的),並且假設heap中沒有掃描到的地區都是閒置,統統refresh(實際上是把stack中丟失了對象地址的無用對象清除了),這就是垃圾收集的過程。
我們首先要搞清楚的是什麼是資料,什麼是指令?然後要搞清楚對象的方法和對象的屬性分別儲存在哪裡?
為了便於描述,我簡單的統稱:
1)方法本身是指令的作業碼部分,儲存在stack中;
2)方法內部變數作為指令的運算元部分,跟在指令的作業碼之後,儲存在stack(簡單類型儲存在stack中,物件類型在stack中儲存地址,在heap中儲存值);
上述的指令作業碼和指令運算元構成了完整的Java指令。
3)對象執行個體包括其屬性值作為資料,儲存在資料區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中的(基本類型儲存在stack中,物件類型地址儲存在stack,值儲存在heap 中),而不同於動態屬性儲存在heap 中。正因為都是在stack中,而stack中指令和資料都是定長的,因此很容易算出位移量,也因此不管什麼指令(類的方法),都可以訪問到類的靜態屬性。也正因為靜態屬性被儲存在stack中,所以具有了全域屬性。
總結一下:靜態屬性儲存在stack指令記憶體區,動態屬性儲存在heap 資料記憶體區。
轉載連結:
http://www.189works.com/article-58284-1.html
http://blog.csdn.net/zhongyili_sohu/article/details/8031410
從JVM記憶體管理的角度談談JAVA類的靜態方法和靜態屬性