慢慢琢磨JVM
1 JVM簡介
JVM是我們Javaer的最基本功底了,剛開始學Java的時候,一般都是從“Hello World”開始的,然后會寫個復雜點class,然后再找一些開源框架,比如Spring,Hibernate等等,再然后就開發企業級的應用,比如網站、企業內部應用、實時交易系統等等,直到某一天突然發現做的系統咋就這么慢呢,而且時不時還來個內存溢出什么的,今天是交易系統報了StackOverflowError,明天是網站系統報了個OutOfMemoryError,這種錯誤又很難重現,只有分析Javacore和dump文件,運氣好點還能分析出個結果,運行遭的點,就直接去廟里燒香吧!每天接客戶的電話都是戰戰兢兢的,生怕再出什么幺蛾子了。我想Java做的久一點的都有這樣的經歷,那這些問題的最終根結是在哪呢?——?JVM。
JVM全稱是Java VirtualMachine,Java虛擬機,也就是在計算機上再虛擬一個計算機,這和我們使用?VMWare不一樣,那個虛擬的東西你是可以看到的,這個JVM你是看不到的,它存在內存中。我們知道計算機的基本構成是:運算器、控制器、存儲器、輸入和輸出設備,那這個JVM也是有這成套的元素,運算器是當然是交給硬件CPU還處理了,只是為了適應“一次編譯,隨處運行”的情況,需要做一個翻譯動作,于是就用了JVM自己的命令集,這與匯編的命令集有點類似,每一種匯編命令集針對一個系列的CPU,比如8086系列的匯編也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集則是可以到處運行的,因為JVM做了翻譯,根據不同的CPU,翻譯成不同的機器語言。
JVM中我們最需要深入理解的就是它的存儲部分,存儲?硬盤?NO,NO,?JVM是一個內存中的虛擬機,那它的存儲就是內存了,我們寫的所有類、常量、變量、方法都在內存中,這決定著我們程序運行的是否健壯、是否高效,接下來的部分就是重點介紹之。
2 JVM的組成部分
我們先把JVM這個虛擬機畫出來,如下圖所示:
從這個圖中可以看到,JVM是運行在操作系統之上的,它與硬件沒有直接的交互。我們再來看下JVM有哪些組成部分,如下圖所示:
?該圖參考了網上廣為流傳的JVM構成圖,大家看這個圖,整個JVM分為四部分:
q??Class Loader?類加載器
類加載器的作用是加載類文件到內存,比如編寫一個HelloWord.java程序,然后通過javac編譯成class文件,那怎么才能加載到內存中被執行呢?Class Loader承擔的就是這個責任,那不可能隨便建立一個.class文件就能被加載的,Class Loader加載的class文件是有格式要求,在《JVM Specification》中式這樣定義Class文件的結構:
????ClassFile {
??????u4 magic;
??????u2 minor_version;
??????u2 major_version;
??????u2 constant_pool_count;
??????cp_infoconstant_pool[constant_pool_count-1];
??????u2 access_flags;
??????u2 this_class;
??????u2 super_class;
??????u2 interfaces_count;
??????u2 interfaces[interfaces_count];
??????u2 fields_count;
??????field_info fields[fields_count];
??????u2 methods_count;
??????method_info methods[methods_count];
??????u2 attributes_count;
??????attribute_infoattributes[attributes_count];
????}
需要詳細了解的話,可以仔細閱讀《JVM Specification》的第四章“The class File Format”,這里不再詳細說明。
友情提示:Class Loader只管加載,只要符合文件結構就加載,至于說能不能運行,則不是它負責的,那是由Execution Engine負責的。
q??Execution Engine?執行引擎
執行引擎也叫做解釋器(Interpreter),負責解釋命令,提交操作系統執行。
q??Native Interface本地接口
本地接口的作用是融合不同的編程語言為Java所用,它的初衷是融合C/C++程序,Java誕生的時候是C/C++橫行的時候,要想立足,必須有一個聰明的、睿智的調用C/C++程序,于是就在內存中專門開辟了一塊區域處理標記為native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies。目前該方法使用的是越來越少了,除非是與硬件有關的應用,比如通過Java程序驅動打印機,或者Java系統管理生產設備,在企業級應用中已經比較少見,因為現在的異構領域間的通信很發達,比如可以使用Socket通信,也可以使用Web Service等等,不多做介紹。
q??Runtime data area運行數據區
運行數據區是整個JVM的重點。我們所有寫的程序都被加載到這里,之后才開始運行,Java生態系統如此的繁榮,得益于該區域的優良自治,下一章節詳細介紹之。
?
整個JVM框架由加載器加載文件,然后執行器在內存中處理數據,需要與異構系統交互是可以通過本地接口進行,瞧,一個完整的系統誕生了!
2 JVM的內存管理
所有的數據和程序都是在運行數據區存放,它包括以下幾部分:
q??Stack?棧
棧也叫棧內存,是Java程序的運行區,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對于棧來說不存在垃圾回收問題,只要線程一結束,該棧就Over。問題出來了:棧中存的是那些數據呢?又什么是格式呢?
棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀F1,并被壓入到棧中,A方法又調用了B方法,于是產生棧幀F2也被壓入棧,執行完畢后,先彈出F2棧幀,再彈出F1棧幀,遵循“先進后出”原則。
那棧幀中到底存在著什么數據呢?棧幀中主要保存3類數據:本地變量(LocalVariables),包括輸入參數和輸出參數以及方法內的變量;棧操作(Operand Stack),記錄出棧、入棧的操作;棧幀數據(FrameData),包括類文件、方法等等。光說比較枯燥,我們畫個圖來理解一下Java棧,如下圖所示:
?圖示在一個棧中有兩個棧幀,棧幀2是最先被調用的方法,先入棧,然后方法2又調用了方法1,棧幀1處于棧頂的位置,棧幀2處于棧底,執行完畢后,依次彈出棧幀1和棧幀2,線程結束,棧釋放。
q??Heap?堆內存
一個JVM實例只存在一個堆類存,堆內存的大小是可以調節的。類加載器讀取了類文件后,需要把類、方法、常變量放到堆內存中,以方便執行器執行,堆內存分為三部分:
Permanent Space?永久存儲區
永久存儲區是一個常駐內存區域,用于存放JDK自身所攜帶的Class,Interface的元數據,也就是說它存儲的是運行環境必須的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉JVM才會釋放此區域所占用的內存。
Young Generation Space?新生區
新生區是類的誕生、成長、消亡的區域,一個類在這里產生,應用,最后被垃圾回收器收集,結束生命。新生區又分為兩部分:伊甸區(Eden space)和幸存者區(Survivor pace),所有的類都是在伊甸區被new出來的。幸存區有兩個:?0區(Survivor 0 space)和1區(Survivor 1 space)。當伊甸園的空間用完時,程序又需要創建對象,JVM的垃圾回收器將對伊甸園區進行垃圾回收,將伊甸園區中的不再被其他對象所引用的對象進行銷毀。然后將伊甸園中的剩余對象移動到幸存0區。若幸存0區也滿了,再對該區進行垃圾回收,然后移動到1區。那如果1區也滿了呢?再移動到養老區。
Tenure generation space養老區
養老區用于保存從新生區篩選出來的JAVA對象,一般池對象都在這個區域活躍。???三個區的示意圖如下:
?q??Method Area?方法區
方法區是被所有線程共享,該區域保存所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。
q??PC Register?程序計數器
每個線程都有一個程序計數器,就是一個指針,指向方法區中的方法字節碼,由執行引擎讀取下一條指令。
q??Native Method Stack?本地方法棧
來自jameswxx 樵夫后院 的博客
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: java线程安全总结
- 下一篇: DNF100级天帝怎么加点 100级天帝