本文作者:黃海燕绵疲,叩丁狼高級(jí)講師。原創(chuàng)文章臣疑,轉(zhuǎn)載請(qǐng)注明出處盔憨。
開篇:有些程序員覺得學(xué)習(xí)jvm是一個(gè)裝逼的行為開發(fā)中用不到,或者是為了應(yīng)付面試而去學(xué)習(xí)的,我覺得如果你是甘于平庸的碼農(nóng)只是寫寫業(yè)務(wù)代碼的,確實(shí)是沒有學(xué)習(xí)的必要的.但是如果你對(duì)你的職業(yè)生涯是有規(guī)劃想往架構(gòu)師、高級(jí)程序員等方向發(fā)展的,或者是解決內(nèi)存泄漏讯沈、gc頻繁導(dǎo)致的程序響應(yīng)慢等問題郁岩,或者是寫出最優(yōu)的代碼,那么了解整個(gè)jvm的執(zhí)行流程缺狠、內(nèi)存模型问慎、gc日志分析、gc算法挤茄、java自帶工具等內(nèi)容是很有必要的如叼。
1.了解什么是jvm?
首先vm指的是虛擬機(jī)(Virtual Machine)指通過(guò)軟件模擬的具有完整硬件系統(tǒng)功能的、運(yùn)行在一個(gè)完全隔離環(huán)境中的完整計(jì)算機(jī)系統(tǒng),比如:VMware,它能在電腦本身的操作系統(tǒng)中模擬出其他獨(dú)立的操作系統(tǒng)穷劈。
而jvm就是java在實(shí)際計(jì)算機(jī)上面仿真模擬各種計(jì)算機(jī)功能來(lái)實(shí)現(xiàn)的,擁有自己完善的精簡(jiǎn)架構(gòu),例如:處理器,堆棧,寄存器和相應(yīng)java指令集的系統(tǒng).
2.jvm內(nèi)存概述
為什么需要需要學(xué)習(xí)jvm內(nèi)存結(jié)構(gòu)?
程序的運(yùn)行時(shí),程序所需內(nèi)存是由jvm本身進(jìn)行管理的,在實(shí)際開發(fā)中經(jīng)常會(huì)出現(xiàn)由于硬件或者是由于程序本身問題出現(xiàn)的內(nèi)存溢出和內(nèi)存泄漏問題,如果我們不了解jvm內(nèi)存結(jié)構(gòu),我們無(wú)法找到問題本身從而去解決這些問題,和會(huì)根據(jù)實(shí)際情況對(duì)程序進(jìn)行優(yōu)化.
程序員編寫了java源文件笼恰,通過(guò)javac命令啟動(dòng)javac工具將源文件編譯成為與平臺(tái)無(wú)關(guān)的class字節(jié)碼文件(編譯過(guò)程不是我們關(guān)心的),class字節(jié)碼文件通過(guò)類加載加載進(jìn)內(nèi)存中囚衔,運(yùn)行時(shí)內(nèi)存分為以下幾部分:
JVM運(yùn)行時(shí)內(nèi)存=線程共享內(nèi)存+線程私有內(nèi)存
所以真要結(jié)合線程挖腰,內(nèi)存應(yīng)該表示如下:
堆:
1)垃圾收集器管理的主要區(qū)域雕沿,可細(xì)分為新生代练湿、老年代,新生代可再細(xì)分為Eden审轮、From Survive肥哎、To Survive空間
2)存放對(duì)象實(shí)例和數(shù)組(特殊對(duì)象)
3)被所有線程共享.
4)內(nèi)存大小可以設(shè)置
方法區(qū):
1)方法區(qū)和永久代(PermGen space)不能等同,因?yàn)樵谄渌膉vm中可能沒有永久代的說(shuō)法疾渣,但是在HotSpot上把GC分代收集擴(kuò)展至方法區(qū)篡诽,我們可以說(shuō)使用永久代來(lái)實(shí)現(xiàn)方法區(qū)。所以為了簡(jiǎn)化理解榴捡,在hotspot中我們可以將方法區(qū)理解為永久代杈女。
2)因?yàn)樵贖otSpot上把GC分代收集擴(kuò)展至方法區(qū),所以方法區(qū)能被gc管理吊圾。
3)方法區(qū)中可以存放已被虛擬機(jī)加載的類信息达椰、常量、靜態(tài)變量项乒、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)啰劲。
4)被所有線程共享。
5)方法區(qū)中包含運(yùn)行中的常量池檀何,用于存放字面量和符號(hào)等蝇裤,但是注意廷支,這些數(shù)據(jù)不僅僅是編譯的時(shí)候產(chǎn)生,運(yùn)行的時(shí)候也能產(chǎn)生常量栓辜,比如String中的intern方法能在運(yùn)行時(shí)產(chǎn)生常量恋拍,所以常量池是可以具有伸縮性的。
6)內(nèi)存大小可以設(shè)置
java棧:
1)棧內(nèi)存為線程私有的空間藕甩,每個(gè)線程都會(huì)創(chuàng)建私有的棧內(nèi)存芝囤,用于存儲(chǔ)棧幀(Frames)
a)局部變量數(shù)組:存放一個(gè)方法執(zhí)行時(shí)的所有變量,包括this引用(當(dāng)前對(duì)象的引用)辛萍、方法參數(shù)悯姊、方法內(nèi)部的局部變量,注意如果對(duì)象方法this存在在局部變量數(shù)組0的位置上贩毕,如果是靜態(tài)方法局部變量數(shù)組0的位置是變量悯许,另外long和double類型的局部變量因?yàn)槭?4位雙精度類型,所以占局部變量數(shù)組兩個(gè)連續(xù)的位置辉阶,其他32位單精度的占一個(gè)位置先壕。
b)操作棧(求知棧):可以理解為JVM線程的工作區(qū),執(zhí)行運(yùn)算的區(qū)域谆甜。為了同學(xué)們能夠了解運(yùn)算過(guò)程垃僚,在網(wǎng)上下了個(gè)動(dòng)圖,如下:
public class Demo {
public static void foo() {
int a = 1;
int b = 2;
int c = (a + b) * 5;
}
}
c)返回值:跳轉(zhuǎn)到當(dāng)前被調(diào)用的地方规辱,但是返回分為兩種情況谆棺,一是正常返回,根據(jù)方法定義返回給調(diào)用者罕袋,二是出現(xiàn)異常改淑,不會(huì)將返回值傳給調(diào)用者。
d)動(dòng)態(tài)鏈接:常量池中查找父類引用指向子類對(duì)象的真實(shí)實(shí)例對(duì)象浴讯。也就是方法覆蓋調(diào)用那個(gè)方法就是動(dòng)態(tài)鏈接決定的朵夏。
2)由于棧中存儲(chǔ)的棧幀會(huì)在方法執(zhí)行完畢后,就會(huì)自動(dòng)出棧釋放內(nèi)存資源榆纽,所以不需要gc管理仰猖。
3)線程私有。
4)棧內(nèi)存大小可以設(shè)置(注意設(shè)置的是棧不是棧幀)奈籽,但是不能設(shè)置太大和太小饥侵,設(shè)置小了,一個(gè)方法調(diào)用創(chuàng)建一個(gè)棧幀唠摹,方法調(diào)用多了爆捞,裝不下那么多棧幀,出現(xiàn)棧溢出勾拉。設(shè)置大了煮甥,多線程的時(shí)候就會(huì)同時(shí)創(chuàng)建很多個(gè)棧內(nèi)存盗温,整個(gè)棧內(nèi)存=線程數(shù)*棧內(nèi)存,導(dǎo)致總棧的內(nèi)存過(guò)大成肘,內(nèi)存溢出卖局。所以一般大小設(shè)置為128kb或者256kb.
本地方法棧:
本地方法棧的功能和特點(diǎn)類似于虛擬機(jī)棧,不同的是双霍,本地方法棧服務(wù)的對(duì)象是JVM執(zhí)行的native方法砚偶,而虛擬機(jī)棧服務(wù)的是JVM執(zhí)行的java方法.,線程私有洒闸。
程序計(jì)數(shù)器:
可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的 行號(hào)指示器染坯,通過(guò)程序計(jì)數(shù)器記錄當(dāng)前執(zhí)行到第幾條java指令。字節(jié)碼指令丘逸,分支单鹿、循環(huán)、跳準(zhǔn)深纲、異常處理仲锄、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來(lái)完成。若執(zhí)行Java方法則計(jì)數(shù)器記錄字節(jié)碼指令地址湃鹊,若執(zhí)行Native方法則計(jì)數(shù)器為Undefined儒喊。線程私有。程序計(jì)數(shù)器是java虛擬機(jī)中唯一一塊不會(huì)產(chǎn)生error的內(nèi)存區(qū)域
出現(xiàn)的錯(cuò)誤:
StackOverflowError:棧溢出錯(cuò)誤
錯(cuò)誤位置 | 錯(cuò)誤原因 |
---|---|
java虛擬棧/本地方法棧 | 1)棻液牵空間設(shè)置太小 2)方法調(diào)用太多導(dǎo)致棧幀太多怀愧,棧放不下,例如:方法無(wú)限遞歸錯(cuò)誤位置 |
OutofMemoryError:內(nèi)存溢出錯(cuò)誤富雅,簡(jiǎn)稱OOM錯(cuò)誤
錯(cuò)誤位置 | 錯(cuò)誤原因 |
---|---|
java虛擬棧/本地方法棧 | 1)椀空間設(shè)置太大 2)線程創(chuàng)建過(guò)多肛搬,椕挥樱總內(nèi)存=線程數(shù)*單個(gè)棧內(nèi)存,當(dāng)椢屡猓總內(nèi)存>(進(jìn)程內(nèi)存-堆-方法區(qū))蛤奢,就會(huì)出現(xiàn)內(nèi)存溢出 |
堆 | 1)不斷的創(chuàng)建對(duì)象,并且保證對(duì)象是被引用的來(lái)避免垃圾回收機(jī)制清除這些對(duì)象 |
方法區(qū) | 1)方法區(qū)中的運(yùn)行常量池運(yùn)行時(shí)可以存放數(shù)據(jù)陶贼,例如String中的intern方法啤贩。存放數(shù)據(jù)內(nèi)存>方法區(qū)內(nèi)存 |
結(jié)束語(yǔ):本章只是作為一個(gè)拋磚引玉的作用,其實(shí)我們還沒有真正深入到j(luò)vm中拜秧,但是要學(xué)習(xí)jvm我們還是要先了解本章的內(nèi)容痹屹,作為學(xué)習(xí)java虛擬機(jī)的基礎(chǔ)。欲知后事請(qǐng)聽下回分解枉氮,敬請(qǐng)期待下一章《第二篇-gc內(nèi)存分配》
想獲取更多技術(shù)干貨志衍,請(qǐng)前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html