久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Java虛擬機(jī)回憶錄

 2017helloworld 2017-07-31

最近蠻閑,,無代碼可擼,,在北京地鐵程序員專線上看著一哥們兒在看周大神的書,腦海就開始回憶一下當(dāng)時看那本書時,,對它的理解,。那首歌唱的好:“你的心有一道墻”,相信很多人在從事Java不久都會對JVM有種神秘感以及對那些人的膜拜,,“他為啥那么厲害,,臥槽,,他對JVM好熟,好屌好屌”,。

那么今天我就來談?wù)勎覍λ睦斫狻?/p>

Java虛擬機(jī)回憶錄

首先,,每當(dāng)說起JVM大概都會想到Java虛擬機(jī)運(yùn)行時數(shù)據(jù)區(qū),那么它是怎樣劃分的呢,?話不多說,,別跟一個直男程序員談理論扯理想,最愛看的就是圖(圖摘自互聯(lián)網(wǎng)),。

很多人粗略的將它分為堆區(qū)和非堆區(qū),,由上圖可以看出Java虛擬機(jī)在執(zhí)行程序時會把他管理的內(nèi)存劃分為不同的數(shù)據(jù)區(qū)。

首先第一個想到的就是它了,,在虛擬機(jī)啟動時就創(chuàng)建,,一坨兒活躍在虛擬機(jī)所管理內(nèi)存中的巨無霸(內(nèi)存最大),被所有線程共享的內(nèi)存區(qū)域,,該區(qū)域唯一的目的就是存放對象實例,,幾乎所有的對象實例以及數(shù)組都要在堆上分配,堆是虛擬機(jī)收集的主要區(qū)域,,基本上都采用的是分代收集算法,,分為:新生代、老年代,,堆大小可通過-Xms,、-Xmx控制,如果堆沒有內(nèi)存來完成實例分配且也無法再擴(kuò)展時,,將會OOM,。

新生代:新創(chuàng)建的對象在此分配,由于新生代中的對象屬于朝生夕死的,,其回收算法采用的是復(fù)制算法,,它由Eden區(qū)和兩個大小相等的From Survivor區(qū)、To Survivor區(qū)構(gòu)成,。當(dāng)Minor GC時,,Eden區(qū)存活的對象將被移到To Survivor區(qū),而From Survivor區(qū)存活的對象根據(jù)其年齡決定,,當(dāng)年齡達(dá)到閥值(-XX:MaxTenuringThreshold可配,,沒記錯的話默認(rèn)15歲就老了)則會進(jìn)入老年代,否則進(jìn)入To Survivor區(qū),,虛擬機(jī)會清空Eden區(qū)和From Survivor區(qū),,最后將To Survivo區(qū)與From Survivor區(qū)互換角色來保證To Survivo區(qū)為空。

Java虛擬機(jī)回憶錄

這里只說下部分調(diào)整年輕代的配置,其它的大家可根據(jù)Sun文檔選擇合適自己的,。

1)-XX:NewSize,、-XX:MaxNewSize 設(shè)置年輕代的大小,建議兩個值設(shè)為一樣大,,一般為整個堆大小的1/3或者1/4,。

2)-XX:SurvivorRatio 設(shè)置Eden和Survivor的比值,默認(rèn)8:1:1,。

3)-XX:+PrintTenuringDistribution 用于顯示每次Minor GC時Survivor區(qū)中各個年齡段的對象的大小,。

4)-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold用于設(shè)置晉升到老年代的對象年齡的最小值和最大值,每扛過一次Minor GC之后,,年齡就加1,。

5)-Xmn 設(shè)置年輕代大小 可代替1) 。整個JVM內(nèi)存大小=年輕代大小 + 年老代大小 + 持久代大小,。持久代一般固定大小為64m,,所以增大年輕代后,將會減小年老代大小,。此值對系統(tǒng)性能影響較大,,官方推薦配置為整個堆的3/8。

老年代:存放的對象比較穩(wěn)定,,主要存放著那些扛過了好幾次Minor GC回收仍然還活著或者特別大(XX:PretenureSizeThreshold 可配)的對象,,當(dāng)老年代的連續(xù)空間無法分配給新進(jìn)入的較大對象時,會觸發(fā)一次Full GC來騰出更多的空間,,F(xiàn)ull GC的代價很高,,會造成Stop the world,所以要減少Full GC次數(shù),。老年代GC采用的是標(biāo)記-清除或者標(biāo)記-整理算法(根據(jù)收集器選擇),。

標(biāo)記-清除算法:Mark-Sweep顧名思義算法分為標(biāo)記和清除兩個步驟,虛擬機(jī)會先標(biāo)記出所有需要回收的對象,,在標(biāo)記完成后統(tǒng)一回收它們,。標(biāo)記過程大概是:對對象進(jìn)行可達(dá)性分析后,發(fā)現(xiàn)木有與GC Roots相連接的引用鏈,,將會第一次被標(biāo)記并會搞一次篩選活動,,活動內(nèi)容就是看看這個對象有沒有必要執(zhí)行finalize方法,如果沒有覆蓋這個方法或者已經(jīng)執(zhí)行過這個方法,,那么虛擬機(jī)會認(rèn)為沒有必要去執(zhí)行,。如果虛擬機(jī)認(rèn)為有必要執(zhí)行這個方法的話,這個對象就會去F-Queue里待著等待一個finalizer線程去執(zhí)行finalize方法,,因為這個方法是脫離死亡的救命草;第二次標(biāo)記是GC對F-Queue進(jìn)行小規(guī)模標(biāo)記,如果對象在finalize方法抓住了那根草(與GC Roots引用鏈上的任何一個對像關(guān)聯(lián)即可),,那么它將被移除即將回收的區(qū)域,,如果沒有抓住那根草那么它基本靠別自行車了。標(biāo)記-清除算法有兩個不足點,,第一就是它的兩個過程效率都不高,,另一個就是空間問題,會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,,會導(dǎo)致分配較大對象時無法申請到足夠的連續(xù)空間從而觸發(fā)一次GC,。

Java虛擬機(jī)回憶錄

復(fù)制算法:它的出現(xiàn)就是為了解決標(biāo)記清除的不足,套路就是將內(nèi)存劃分為兩個等量大小的塊兒,,對象都在其中一塊兒上,,當(dāng)這一塊兒造完了就將存活的對象復(fù)制到另一塊兒上,然后將剛剛那塊兒一次清理掉,,這樣就不需要考慮內(nèi)存碎片問題,,動動指針按順序非配就搞定了,實現(xiàn)簡單效率高,,但是代價有點大內(nèi)存直接干了一半,,適用于對象存活率低的區(qū)域,比如朝生夕死的新生代,。

Java虛擬機(jī)回憶錄

標(biāo)記-整理算法:復(fù)制算法看起來很吊,,但是對于對象存活率高的區(qū)域就顯得力不從心了,而且如果不想浪費(fèi)一半的空間的話,,就需要進(jìn)行空間分配擔(dān)保(抵押貸款),,所以老年代不能這么搞,進(jìn)而出現(xiàn)了標(biāo)記-整理算法,,套路跟標(biāo)記-清除一樣,,只是不直接清理可回收的對象,而是存活的往一邊兒移動,,然后根據(jù)分界線去干掉另一邊兒,,可以看出該算法要進(jìn)行對象的移動,成本相對略高,,但好處則是不會產(chǎn)生內(nèi)存碎片,。

Java虛擬機(jī)回憶錄

方法區(qū)

方法區(qū)多數(shù)人認(rèn)為的永久代,方法區(qū)與堆一樣是線程共享的內(nèi)存區(qū)域,,類使用要經(jīng)過加載,、連接(驗證、準(zhǔn)備,、解析)和初始化,,加載后的類信息就存在方法區(qū)特定的數(shù)據(jù)結(jié)構(gòu)中,,主要包括:類的全路徑名包括超類(如果這個類是Object則它沒有超類)、類的類型,、類的訪問修飾符,、直接接口全限定名的有序列表、運(yùn)行時常量池(類版本,、字段,、方法信息、常量,、類靜態(tài)變量,、裝載器信息) 等等。由于線程都共享方法區(qū),,所以方法區(qū)的數(shù)據(jù)必須時線程安全的,,如果有2個甚至多個線程同時訪問某個類,而這類又沒被JVM加載,,那么JVM只允許一個線程去加載(雙親委派),,其它線程必須等待。方法區(qū)的內(nèi)存不一定是連續(xù)的,,可以動態(tài)擴(kuò)展大小,,可以選擇不實現(xiàn)GC,GC的目標(biāo)主要是常量池的回收和類型的卸載,,所以想想就好沒多少便宜可撿,,因為回收條件比較苛刻,當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時將OOM(String.intern()是個好例子),。

廣告時間:

ps:臥槽,,好久沒寫了,感覺比擼代碼還難,!PM,,給我一個需求我還你一個美好的明天,放心,,不加班,。

上面說完了線程共享區(qū)域,接來下逼叨逼叨線程隔離數(shù)據(jù)區(qū),。

程序計數(shù)器

程序計數(shù)器屬于線程私有的,,它是當(dāng)前線程所執(zhí)行字節(jié)碼的指示器(執(zhí)行到那兒了),它是一塊較小的內(nèi)存空間,,線程下一步該干撒就是通過字節(jié)碼解釋器改變計數(shù)器來執(zhí)行的,,每個線程都有自己的程序計數(shù)器,多線程就是輪流切換它來實現(xiàn),,Java方法記錄的是虛擬機(jī)字節(jié)碼指令地址,,Native方法沒有記錄,,程序計數(shù)器在JVM中是唯一一個沒有定義OOM的區(qū)域。

虛擬機(jī)棧

如程序計數(shù)器一樣,,Java虛擬機(jī)棧也屬于線程私有,,所以它的生命周期與線程一樣,。它屬于Java方法執(zhí)行的內(nèi)存模型,,每個方法執(zhí)行都會創(chuàng)建一個棧幀,主要存儲著方法出口信息,、局部變量表,、操作數(shù)棧、動態(tài)鏈接,。當(dāng)線程請求的棧幀深度大于虛擬機(jī)所允許的深度會SOF,,若虛擬機(jī)棧動態(tài)擴(kuò)展時無法申請到足夠的內(nèi)存會OOM。

方法出口信息:正常方法返回時可能需要在棧幀中保存一些信息,,用來幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài),,如果有返回值,則把它壓入調(diào)用者棧幀的操作數(shù)棧中,,調(diào)整計數(shù)器的值以指向方法調(diào)用指令后面的一條指令,,若方法異常退出,那么返回地址是通過異常處理器來確定的,,棧幀中一般不會保存這部分信息,。

局部變量表:所需的內(nèi)存空間在編譯期確定,一旦確定無法更改大小,,它存放著編譯期的各種基本數(shù)據(jù)類型,、reference類型(可能是對象引用指針,也可能是個句柄),、returnAddress類型(指向某條字節(jié)碼指令的地址),。

操作數(shù)棧:棧幀剛創(chuàng)建時,操作數(shù)棧是沒有數(shù)據(jù)的,,當(dāng)執(zhí)行方法操作時,,會存放從局部變量表復(fù)制的常量或者變量,包括方法入?yún)⒑头祷刂?,操作?shù)棧都一個固定的棧深度,,入棧按先進(jìn)后出方式,最大深度由編譯期確定,,基本類型除了long,double用2個深度,,其他都用一個。

動態(tài)鏈接:class的常量池中存在有大量的符號引用,,字節(jié)碼中的方法調(diào)用指令就以常量池中指向方法的符號引用為參數(shù),,這些符號引用分為兩種,,一種就是類加載的時候,靜態(tài)解析的那些final 和static代碼塊,,得到的直接引用,,還有一種是運(yùn)行期間轉(zhuǎn)化的(每個棧幀都包含一個指向運(yùn)行時常量池中該棧幀所屬方法的引用),這種就是動態(tài)鏈接,。

本地方法棧

跟虛擬機(jī)棧的作用是一個屌樣,,唯一區(qū)別就是虛擬機(jī)棧是為字節(jié)碼服務(wù)的,而它是為Native方法服務(wù),,與虛擬機(jī)棧一樣,,當(dāng)線程請求的棧幀深度大于虛擬機(jī)所允許的深度會SOF,若虛擬機(jī)棧動態(tài)擴(kuò)展時無法申請到足夠的內(nèi)存會OOM,。

直接內(nèi)存

Direct Memory 雖然不屬于虛擬機(jī)運(yùn)行數(shù)據(jù)區(qū),,但在被NIO引入后一直頻繁使用(比如堆外緩存),可以用Native方法直接分配堆外內(nèi)存,,然后在堆中去引用這塊兒區(qū)域(DirectByteBuffer就是),,如果動態(tài)擴(kuò)展內(nèi)存時達(dá)到物理內(nèi)存限制會OOM。

內(nèi)存分配策略以及類加載機(jī)制以后再補(bǔ),,先寫到這兒吧,,未完待續(xù)!

學(xué)習(xí)Java的同學(xué)注意了?。,。?/p>

學(xué)習(xí)過程中遇到什么問題或者想獲取學(xué)習(xí)資源的話,,歡迎加入Java學(xué)習(xí)交流群495273252,,我們一起學(xué)Java!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多