jvm结构
優(yōu)化是一個(gè)體系,不是一個(gè)點(diǎn),特別是JVM的優(yōu)化比SQL的優(yōu)化還要復(fù)雜,它的顆粒度更細(xì),比如你對(duì)垃圾回收器的選擇,比如對(duì)存儲(chǔ)結(jié)構(gòu)區(qū)域的劃分.而且不同的情況所優(yōu)化的方式不一樣,包括像我們數(shù)據(jù)庫(kù)優(yōu)化,數(shù)據(jù)庫(kù)優(yōu)化可以更換引擎,根據(jù)對(duì)表操作的不同,如果表查詢(xún)特別多,在設(shè)計(jì)表的時(shí)候這個(gè)表就是用來(lái)做查詢(xún)的,換成MYISAM的引擎查詢(xún)效率要比InnoDB要高的.其實(shí)虛擬機(jī)是分為兩個(gè)結(jié)構(gòu)的,一個(gè)是client結(jié)構(gòu),一個(gè)是server結(jié)構(gòu),這是我們從來(lái)都不知道的.1. JVM結(jié)構(gòu)2. JVM垃圾回收算法及收集器3. JVM優(yōu)化4. Eclipse運(yùn)行調(diào)優(yōu)前面兩個(gè)是JVM的原理,第三個(gè)是JVM調(diào)優(yōu)的工具,第四個(gè)是一個(gè)小實(shí)戰(zhàn).還記得我們裝完JDK以后配置環(huán)境變量,我們會(huì)在命令行窗口執(zhí)行 java 或者 java -version這個(gè)步驟就猶如我們打開(kāi)電腦連接網(wǎng)絡(luò)以后想判斷它是否連接成功,打開(kāi)瀏覽器輸入www.baidu.com一樣
以往我們只關(guān)注下面,只要不報(bào)既不是內(nèi)部也不是外部命令可執(zhí)行文件,或者看到這樣的結(jié)果就完事了,立馬把窗口關(guān)掉,但是我們有沒(méi)有認(rèn)真的觀察這三行代碼里面到底涵蓋了什么,我們還是要從這里分析.它會(huì)給你輸出三行結(jié)果:第一行: java version 1.8.0_151 說(shuō)明你當(dāng)前JDK的版本是1.8的 這副版本是151然后再往下 Java<TM> SE Runtime Environment <build 1.8.0_151> 這是顯示開(kāi)發(fā)環(huán)境的一些信息.Java HotSpot<TM> 64-Bit Server VM <build 25.151-b12, mixed mode> 這里我會(huì)看到兩個(gè)比較關(guān)鍵的詞語(yǔ),一個(gè)是HotSpot,一個(gè)是Server, HotSpot是JVM的一個(gè)核心組件,或者叫JVM的名稱(chēng),JDK每次版本再更新的時(shí)候,都會(huì)側(cè)重的去優(yōu)化一個(gè)問(wèn)題,進(jìn)一步的去優(yōu)化JVM的運(yùn)行效率,運(yùn)行效率指的是什么呢?對(duì)class文件的解析或者是編譯,在JDK1.3之前的版本,這是在JDK1.3以后理論提出來(lái)的,JDK1.3之前的版本的核心不是HotSpot,那它會(huì)采取一個(gè)什么樣的策略來(lái)讀取字節(jié)碼文件呢,當(dāng)你運(yùn)行一個(gè)class的時(shí)候,classloader將類(lèi)加載到內(nèi)存虛擬機(jī)當(dāng)中,但它加載進(jìn)來(lái)的是class文件,class文件是一個(gè)中間層文件,它并不是一個(gè)CPU能夠直接解釋的這樣一個(gè)文件虛擬機(jī)需要把class文件編譯成本地代碼,本地代碼就是CPU能夠直接處理的文件,所以每次我們加載進(jìn)來(lái)的class,JVM都會(huì)做一個(gè)本地代碼的轉(zhuǎn)換,每次來(lái)都轉(zhuǎn)換,轉(zhuǎn)換的越多,就表示消耗的性能就越大,那么在JDK1.3這個(gè)版本之后,其實(shí)SUN公司已經(jīng)發(fā)現(xiàn)了這個(gè)問(wèn)題,后來(lái)就引入HotSpot核心技術(shù)的VM,它也是一個(gè)虛擬機(jī),但是HotSpot的VM并不是SUN公司去寫(xiě)的,而是SUN公司收購(gòu)的另一家公司的,因?yàn)樵谶@個(gè)VM里面,它有很多比較先進(jìn)的理念,熱點(diǎn)探測(cè), HotSpot就是一個(gè)熱點(diǎn)探測(cè),你每次虛擬機(jī)要加載class文件,我會(huì)對(duì)你加載的class文件做標(biāo)記,標(biāo)記的過(guò)程當(dāng)中如果達(dá)到一定的閥值,它會(huì)對(duì)頻繁使用的class文件直接編譯成本地代碼緩存起來(lái)了,不再讓你頻繁的編譯了,有點(diǎn)像緩存,跟它的原理上差不多,也就是說(shuō)它會(huì)把它的結(jié)果集放在緩存里,以便你下次再來(lái)的時(shí)候不需要再查數(shù)據(jù)庫(kù)了,它也是,會(huì)把本地代碼會(huì)緩存起來(lái),下次再來(lái)的時(shí)候可以去掉編譯的環(huán)節(jié),HotSpot的特點(diǎn)當(dāng)然還有其他的特點(diǎn),那些可能對(duì)于我們來(lái)說(shuō)就不太關(guān)注,HotSpot在JDK1.3的時(shí)候就準(zhǔn)備要往里放了,任何一個(gè)技術(shù)在商業(yè)化之前它都要經(jīng)過(guò)嚴(yán)格的測(cè)試,所以在JDK1.3并沒(méi)有把HotSpot放在里面,而是在JDK1.5的那個(gè)版本才真正的把HotSpot的技術(shù)嵌入到或者說(shuō)徹底商業(yè)化了.從JDK1.5以后的都是HotSpot的核心, Client是客戶(hù)端, JVM虛擬機(jī)在啟動(dòng)時(shí)它給我們準(zhǔn)備了兩個(gè)版本,一個(gè)是client,一個(gè)是server,但是其實(shí)他們不是兩套虛擬機(jī),是一個(gè)虛擬機(jī),是采用了兩套不同的機(jī)制來(lái)初始化,client更多的是做桌面型的應(yīng)用而提供的一個(gè)虛擬機(jī),也就是說(shuō)應(yīng)用于桌面級(jí)應(yīng)用的虛擬機(jī),對(duì)內(nèi)存空間分配做的優(yōu)化,我們都知道JAVA語(yǔ)言都可以寫(xiě)C/S結(jié)構(gòu)的東西,client的最大特點(diǎn)是什么呢?桌面型的操作系統(tǒng), JVM處理的代碼量并不會(huì)很大,一個(gè)桌面系統(tǒng)打開(kāi)以后,你的按鈕一次只能觸發(fā)一個(gè),你能同時(shí)觸發(fā)兩個(gè)按鈕嗎?程序和JVM交互的頻率相對(duì)于B/S結(jié)構(gòu)來(lái)講,它的頻率會(huì)低很多,雙十一瞬間會(huì)有好大的請(qǐng)求量,我們的代碼要嵌入虛擬機(jī)里面運(yùn)行,虛擬機(jī)要處理的線程數(shù)是不是要多一些,但是桌面級(jí)操作系統(tǒng),一次只能點(diǎn)一個(gè)按鈕,相比于B/S結(jié)構(gòu),讓JVM去處理的線程數(shù),肯定要少很多,所以為了更合理的使用內(nèi)存,在client版本當(dāng)中,它把內(nèi)存分配的空間,相比server端要少了,比如說(shuō),它把堆里的內(nèi)存就分了10幾M,或者100多M,他覺(jué)得十幾兆一百多兆足夠用了,在十幾兆和一百多兆的區(qū)域內(nèi),我所耗的時(shí)間就是0.00幾秒,完全夠用,但是如果是server端,那分配的空間就可能更大,那么JVM啟用的都是客戶(hù)端,那我們能不能把它改成server端,可以的,怎么改呢?
-client和-server誰(shuí)在上就以誰(shuí)啟動(dòng),由上至下來(lái)看,我們也可以手動(dòng)將它的位置來(lái)做一些改變,它修改需要管理員的權(quán)限
虛擬機(jī)默認(rèn)的,不管是client端還是server端,默認(rèn)的已經(jīng)可以很好的適應(yīng)了常見(jiàn)的JAVA的運(yùn)行了.除非你的項(xiàng)目的體系比較強(qiáng)大,你才需要對(duì)它進(jìn)行進(jìn)一步的優(yōu)化,把client改成server版本,server端這么好,為什么不默認(rèn)的使用server端,因?yàn)閟erver版本的比client版本申請(qǐng)的空間要大,比較浪費(fèi)空間,所以在當(dāng)時(shí)那個(gè)年代內(nèi)存比較緊張的時(shí)候,不是占內(nèi)存越大越好,所以給你一個(gè)選擇性,現(xiàn)在這兩個(gè)問(wèn)題已經(jīng)解決了.這是JVM的一個(gè)開(kāi)始,接下來(lái)我們來(lái)看JVM的結(jié)構(gòu):我們要向理解虛擬機(jī),首先還是得理解結(jié)構(gòu).JVM結(jié)構(gòu)第一部分JVM的總體結(jié)構(gòu):
這個(gè)其實(shí)劃分的顆粒度也不是很細(xì),但是這個(gè)顆粒度已經(jīng)足夠我們認(rèn)識(shí)了,JVM在運(yùn)行過(guò)程當(dāng)中,會(huì)分配這么幾個(gè)區(qū)域,第一個(gè)類(lèi)加載子系統(tǒng),第二個(gè)方法區(qū),第三個(gè)JAVA堆,第四塊直接內(nèi)存,第五塊垃圾回收器,第六塊執(zhí)行引擎,這每一塊都代表什么意思呢?1. 類(lèi)加載子系統(tǒng)和方法區(qū): 類(lèi)加載子系統(tǒng)負(fù)責(zé)從文件或者網(wǎng)絡(luò)中加載class文件,也就是說(shuō)classloader也就是一個(gè)組件,其實(shí)不是加載進(jìn)來(lái)直接就運(yùn)行了,它會(huì)經(jīng)過(guò)好多模塊的處理,文件的校驗(yàn),還有HotSpot的處理等等加載的信息存放于一塊稱(chēng)為方法區(qū)的一塊內(nèi)存空間,也就是說(shuō)classloader通過(guò)IO把我們的class文件里面的字節(jié)加載到虛擬機(jī)當(dāng)中的時(shí)候它要保存起來(lái),它不能加載完就扔掉了,那class文件的字節(jié)會(huì)保存到哪呢?會(huì)放在一個(gè)叫方法區(qū)的空間里面. 除了類(lèi)的信息以外,方法區(qū)中可能還會(huì)存放運(yùn)行時(shí)的常量池信息,常量池就是存放我們的常量,都是唯一一份放著,包括字符串字面量和數(shù)字常量,這部分常量信息是class文件中常量池部分的內(nèi)存映射.所以說(shuō)這兩塊的能力通過(guò)類(lèi)加載系統(tǒng)將我們class文件加載進(jìn)來(lái)放到方法區(qū)里,放在方法區(qū)的都是唯一的東西.2. java堆: 這個(gè)java堆就是我們?cè)谥vSE的時(shí)候的堆棧. java堆在虛擬機(jī)啟動(dòng)時(shí)就建立了,它是java程序主要的內(nèi)存工作區(qū)域,幾乎所有的java對(duì)象實(shí)例都存放在java堆中,堆空間是所有線程共享的,這是一塊與java應(yīng)用密切相關(guān)的內(nèi)存空間. 這里是我們要了解的重點(diǎn),我們都是以他作為主講解,我們所有創(chuàng)建的對(duì)象,都會(huì)在堆里出現(xiàn),那么所指的堆就是這個(gè)堆.3. 直接內(nèi)存: java的NIO庫(kù)允許java程序使用直接內(nèi)存,直接內(nèi)存是java堆外的,直接向系統(tǒng)申請(qǐng)內(nèi)存空間,通常訪問(wèn)直接內(nèi)存的速度優(yōu)于java堆. 因此出于性能的考慮,讀寫(xiě)頻繁的場(chǎng)合可能會(huì)考慮使用直接內(nèi)存,由于直接內(nèi)存在java堆外,因此它的大小不會(huì)直接受限于Xmx指定的最大堆大小,但是系統(tǒng)內(nèi)存是有限的,java堆和直接內(nèi)存的的總和依賴(lài)依然受限于操作系統(tǒng)能給出的最大內(nèi)存. 其實(shí)直接內(nèi)存并不隸屬于java堆的范疇內(nèi),也就是說(shuō)它并不是java堆所包含的, java堆的大小是由JVM來(lái)控制的,它說(shuō)多大就多大,而直接內(nèi)存是直接映射到系統(tǒng)內(nèi)存上的,它理論上的空間是無(wú)限的,不受JVM堆的控制,但是實(shí)際上也不可能是無(wú)限的,它的大小取決于你物理內(nèi)存的大小,你說(shuō)你物理內(nèi)存是1G的,剩余的可以給他分配多少多少,但是它的空間大小要比這大一堆的.這就是直接內(nèi)存,NIO庫(kù)下的一個(gè)東西,提升效率的.4. 垃圾回收系統(tǒng): 這個(gè)也是一個(gè)重點(diǎn)內(nèi)存,是java虛擬機(jī)的重要組成部分,垃圾回收器可以對(duì)方法區(qū),java堆和直接內(nèi)存進(jìn)行回收,也就是說(shuō)垃圾回收系統(tǒng)并不局限于回收java堆,對(duì)于方法區(qū)和直接內(nèi)存都是可以回收的,其中java堆是垃圾回收器的重點(diǎn),和C/C++不同,java中所有的對(duì)象空間釋放都是隱式的,也就是對(duì)于程序員來(lái)講對(duì)于內(nèi)存的開(kāi)辟和釋放完全是不透明的,他不像C語(yǔ)言或者C++,也就是說(shuō),java中沒(méi)有類(lèi)似free()或者delete()這樣的函數(shù)釋放指定的內(nèi)存區(qū)域,對(duì)于不再使用的垃圾對(duì)象,垃圾回收系統(tǒng)會(huì)在后臺(tái)默默的工作,默默的查找,標(biāo)記并釋放對(duì)象,完成包括 java堆、方法區(qū)和直接內(nèi)存中的全自動(dòng)管理. 我們要了解垃圾回收,以便與以后我們真的要做優(yōu)化,跟別人學(xué)習(xí)優(yōu)化的時(shí)候,你知道優(yōu)化的點(diǎn),其中垃圾回收就是一個(gè)優(yōu)化的點(diǎn).5. java棧: 每一個(gè)java虛擬機(jī)線程都有一個(gè)私有的java棧,一個(gè)線程的java棧在線程創(chuàng)建的時(shí)候就創(chuàng)建了,java棧中保存著幀信息,其實(shí)所謂的java棧,比如我們講遞歸的時(shí)候,要想把遞歸講的透徹一些,我們肯定要畫(huà)棧圖,因?yàn)榉椒ǘ际窃跅_\(yùn)行的,或者說(shuō)是跟棧有關(guān)的,這個(gè)時(shí)候我機(jī)會(huì)發(fā)現(xiàn),方法自己調(diào)用自己的時(shí)候,這個(gè)方法壓棧壓棧,但是當(dāng)一個(gè)新的方法壓棧的時(shí)候,由于原來(lái)的方法還沒(méi)有運(yùn)行完,有些數(shù)據(jù)需要保存,這個(gè)保存點(diǎn)就叫棧幀,java棧中保存著局部變量,方法參數(shù),也就是說(shuō)所有的局部變量和參數(shù)也叫做棧變量,原因就在這里,它是獨(dú)立的,棧和棧中間是完全隔離的,同時(shí)和java方法的調(diào)用和返回密切相關(guān).6. 本部方法棧: 本地方法棧和java棧非常類(lèi)似,也是一個(gè)棧空間,最大的不同在于java棧用于方法的調(diào)用,而本地方法棧則用于本地方法的調(diào)用,本地方法就是native method, 也就是說(shuō)虛擬機(jī)在運(yùn)行一些內(nèi)容的時(shí)候,其實(shí)脫離不了操作系統(tǒng)的內(nèi)容,也就是要調(diào)用操作系統(tǒng)的API,這個(gè)API在JVM里就叫做本地方法,為什么不同的操作系統(tǒng)提供了不同版本的JVM,就是因?yàn)樵谶@一塊的位置上不通用,因?yàn)椴煌牟僮飨到y(tǒng)的結(jié)構(gòu)或者方法是不一樣的,所以為了讓虛擬機(jī)能夠適應(yīng)各個(gè)操作系統(tǒng),那么就必須寫(xiě)多個(gè)虛擬機(jī)調(diào)用多個(gè)操作系統(tǒng)里的不同的方法來(lái)完成它想要做的事,如果所有的操作系統(tǒng)本地方法都相同,那虛擬機(jī)只要有一份就可以了,java虛擬機(jī)允許java直接調(diào)用本地方法(通常使用C編寫(xiě))7. PC寄存器(Program Counter): PC寄存器是每一個(gè)線程私有的空間,java虛擬機(jī)會(huì)為每一個(gè)java線程創(chuàng)建PC寄存器,在任意時(shí)刻, 一個(gè)java線程總是在執(zhí)行一個(gè)方法, 這個(gè)正在執(zhí)行的方法稱(chēng)為當(dāng)前方法,如果當(dāng)前方法不是本地方法, PC寄存器就會(huì)指向當(dāng)前正在被執(zhí)行的指令,如果當(dāng)前方法是本地方法,那么PC寄存器的值就是undefined, PC寄存器是區(qū)別線程里執(zhí)行的方法是本地方法還是java自己的方法的作用.8. 執(zhí)行引擎: 執(zhí)行引擎是java虛擬機(jī)的最核心組件之一,它負(fù)責(zé)執(zhí)行虛擬機(jī)的字節(jié)碼,現(xiàn)在虛擬機(jī)為了提升效率會(huì)使用即時(shí)編譯(just in time)技術(shù)將方法編譯成機(jī)器碼后再執(zhí)行.
?
總結(jié)
- 上一篇: ThreadLocal - Java多线
- 下一篇: jvm堆分代