JVM體系架構分析與內存原理模式詳解~新

世界上有一半以上的程序員在使用JAVA編程,然後有一般以上的服務都採用JAVA虛擬機來運行,然後,我們真正對它瞭解有多少,多數人在編碼的過程中可能都會忽視這個問題,當然也包括我,做了這麼多年的程序員,寫了也不少代碼,但問題是,回想起來個人對其理解的程度也不咋的,所以今天利用這個時間來對JVM簡單的描述下,我們既然要了解JAVA虛擬機,當然我們首先應該去了解它的體系結構,以及運行原理,體系結構的話,我在這裏就不畫圖了,網上有很多,在這裏,我也CP一張吧,這下面的圖是正確的:




從總體結構上看,上面可大體分爲五部分來體現,第一部分是類裝載子系統,第二部分是運行數據區,第三部分是執行引擎,第四部分是本地方法接口,第五部分就是本地方法庫。OK,這是總體結構的劃分,細分每個結構吧:
第一部分:類裝載子系統,主要的工作就是爲加載類文件,負責把相關的類加載進來
第二部分:運行數據區,可能這也是JVM裏包括的內容最多也最複雜的部分,爲什麼呢,我們細下來看圖,圖中分爲五小部分,方法區,JAVA棧,JAVA堆,程序計數器,本地方法棧。
好,現在我們來詳細分解下這個所爲最複雜的部分,首先我們來理解,我們爲什麼需要這個方法區的存在,想想方法區的字面含義理解應該是信息存儲的展示區吧,不知道我的理解對不對,也就是說在我們通過類裝載子系統把相關的類裝載進運行數據區時,其類會經過一個編譯器編譯的過程,當編譯器編譯後會出現一些相關的信息,如屬性,常量,方法等,然而這些信息進入運行數據區後存放位置如何呢,首先它們考慮了方法區,畢竟方法去就是用來存放其相關信息的,然而此時並可能不會有任何堆棧內存的分配,只是還需要進一步去走流程與分解,這時一旦信息進入方法去後,想想,檢查下是否有相關的指令集的聲明,如JAVA的基本數據類型,指令代碼,以及常量等,若有就要求JAVA棧分配其定長空間來存儲,在這裏說到棧了,我們來理解下棧,什麼是JAVA棧呢,簡單的來說,JAVA棧其實就是一個定長內存指令存儲區域,它的優點是什麼,優點有,存儲速度快,對內存管理簡單,對指令集內存分配爲定長分配,而且是按順序存儲的,即以壓棧與彈棧的方式來進行簡單操作,還有沒有別的優點呢,先別急,等我們看來JAVA堆對比以下就知道了,前面說到類裝載後編譯的信息首先存儲在方法區,其次以選定的方式進行對棧作來相應分配,當然,這時候還會作另一個檢測,就是該類是否有對其實例對象,若有,不好意識,實例對象對內存分配是屬於動態改變的,想要棧來支持,沒門,棧不答應,因爲那樣違反了棧的存儲原則,沒辦法就只能去找JAVA堆了,因爲聽說只有JAVA堆才支持內存動態分配,OK,JAVA堆答應了,但是問題來,你這個實例對象咋就那麼千變萬化呢,你的數據量總是在變化,而且有時候我需要給你分配很大的內存空間,甚至你不用了,你還棧着的可能性都有,不行,這是堆得想辦法對其實例對象進行檢查,要是我給其分配的內存空間,它不用了,咋就給它收回來,這樣也可以免得過分浪費資源嘛,這時候堆就整了個GC(垃圾回收)機制,GC主要是來監控其在堆中實例的對象是否一直佔用其內存,若沒有,就對其收回,想到這裏,在堆中實例的都是動態數據啊,沒有相應的標記來檢查,而且堆中的內存是屬於其所有線程所共享,這可怎麼辦,GC後面想了個辦法,棧不是存放指令的地方嗎,而且管理與操作都很簡單,又效力,想想,可以這樣做,每到堆棧中實例的對象,都需要在棧中存儲一個執行堆中實例對象的4字節指令碼,它們是一一對應的,這樣,我就可以通過在棧中的指令集來很快速與方便的查找到在堆中實例的對象數據了,OK,問題解決了,相對於說,在棧中的指令指向堆中的的實例,這個指令就好比是一個地址指針,就類似於一個信封地址一樣,在這裏我們也就把堆給順便講解了,在細一點吧,在JAVA編碼中,我們長會用static這個修飾符,在JAVA中也就是所爲的靜態與非靜態的區別,然而我們怎麼去區分它們呢,這可能需要我們去結合JAVA中的堆棧來進行講解,大家都知道,靜態方法是可以直接調用的,而非靜態方法是需要實例才能訪問的,在我們細下里看DALVIK的源碼時,都會發現在需要實例的非靜態方法時會有一個默認的隱含參數的操作,當然這個隱含參數不是自己給的,是由JVM來分配的,沒實例一個非靜態方法,JVM都會給該實例在STACK中給出一個默認的隱含參數指令來指向堆中實例化了的對象,這樣就可以通過這個指令來尋找到在對象動態實例的對象了,當然,這裏我們可能還會牽涉到程序計數器,它做什麼用的呢,它主要是對其字節碼的行號作指示器,也就是說,在一個class文件被load進虛擬機後,方法指令保存在棧中,堆中此刻是沒有數據的,此時程序計數器開始執行指令,如果是靜態方法就直接執行指令代碼,此時指令代碼是不能訪問堆中的數據區域的,如果是非靜態方法,由於隱含參數沒有值,也就無法通過棧中的指令去檢查到堆中的數據,也就會報錯,這時候必須對其非靜態方法進行實例纔是,然後把在棧中分配的指令給非靜態方法,這樣程序計數器就依次執行指令就檢查到堆中實例的相應數據區域了。在這裏,我們需要理解下靜態方法與動態屬性,然動態屬性需要棧中的指令才能發現去堆中實例對象數據區域,在這裏我們不難知道,靜態屬性可能也是放在棧中存儲了,這樣定長的數據很容易算出偏移量,爲此不管類的什麼方法,都可以訪問到類的靜態屬性,此就成來全局屬性的稱謂,當然實例對象的方法指令也是存儲到棧中的,這點得稍微注意以下。剩下的就是本地方法棧了,它我們又該怎麼去理解呢,其實這個本地方法棧很好理解,簡單的描述就是服務於本地方法的通信方式。
            好了上面是對運行數據區的理解,還有執行引擎,與本地方法接口和本地方法庫,這幾個問題,我就不詳細講解,因爲它們存在的理由很簡單就是一個適配過程。
            介紹完以上,我們就結合之前講解的策略模式來對其簡單的描述下吧,當然我的理解並不一定正確,只是拿來作參考罷了,前面我們講解的策略模式是以錦囊妙計的話題來展開講解的,用在這裏也無妨,類似於JVM的運行數據區就是一個錦囊,它包括了很多妙計的執行,只是一般的妙計的是獨立執行的,但最終都有一個目的就是去完成這總的過程纔是,如在錦囊妙計中,目的就是讓劉備去江東安全的把老婆娶回來,然JVM中的過程目的就是把裝載的類穩定的走起來,並獲得結果,類似於方法區,程序計數器,棧,堆,本地方法棧等都是其妙計之一,只是功能表現不同罷了,OK,在執行引擎相對來說就是趙雲,二類裝載器好像就是諸葛亮(這個比喻可能不太好),其本地方法接口就類似於執行操作規則,OK,這樣比喻不知道正確否,說了這麼多,唯一想表達的是,我們在編碼過程中不僅僅只是知道代碼可以編出來就行了,還要去理解它爲什麼要這麼編,這也算是我多年來的經驗總結吧,尤其是在JAVA中我們可能太少去考慮內存分配與算法優化等問題,我這些在以後我的程序生涯中需要隨時警惕纔是,因爲我一生的夢想就是做一個真正合格的軟件工程師,二不僅僅只是一個會寫代碼的程序員,我想我們都應該是,在下節,我順便講解下JVM的GC吧,
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章