JVM內(nèi)存模型

從內(nèi)存模型開始說起:作為一般人需要了解到的榔袋,JVM的內(nèi)存區(qū)域可以被分為:線程棧,堆遗增,靜態(tài)方法區(qū)(實(shí)際上還有更多功能的區(qū)域张咳,并且這里說的是JVM的內(nèi)存區(qū)域,實(shí)際上Java程序還可以調(diào)用native方法使用直接內(nèi)存)似舵。

?關(guān)于幾個(gè)分區(qū)的描述定義

1.線程棧:線程創(chuàng)建是會(huì)為每個(gè)線程創(chuàng)建一個(gè)線程棧脚猾,線程棧里面會(huì)為每個(gè)方法調(diào)用創(chuàng)建一個(gè)棧幀。主要用于保存線程的當(dāng)前運(yùn)行狀態(tài)砚哗。

2.堆:用于存放運(yùn)行時(shí)中生成的新對(duì)像龙助。會(huì)劃分成新生代和老年代。新生代里面又劃分成了eden區(qū)蛛芥、存活1區(qū)和存活2區(qū)提鸟。

3.永久區(qū):方法和常量區(qū),用于存放方法字節(jié)碼元數(shù)據(jù)和各種常量仅淑。


為什么堆會(huì)劃分為新生代和老年代称勋?

基本原理:對(duì)于大部分應(yīng)用,常駐對(duì)象不多涯竟。因?yàn)榇蟛糠执婊顗勖婚L(zhǎng)赡鲜,新生代和老年代的劃分有利于區(qū)分對(duì)待和縮小垃圾回收范圍。(Most allocated objects are not referenced (considered live) for long, that is, they die young. Few references from older to younger objects exist.)

本文接下來就重點(diǎn)說說這三個(gè)區(qū)域庐船。

1. 線程棧

簡(jiǎn)介

注意這個(gè)棧和數(shù)據(jù)結(jié)構(gòu)中的stack有相似之處银酬,但并不是用戶態(tài)的。準(zhǔn)確的講它壓入的每個(gè)棧幀(Stack Frame)是程序指令以及局部變量表筐钟,每個(gè)方法調(diào)用對(duì)應(yīng)一個(gè)棧幀。局部變量表包括各種基本數(shù)據(jù)類型:boolean、byte诅愚、char瘪弓、short、int纹因、float喷屋、long、double以及對(duì)象的引用瞭恰。我們需要注意到每個(gè)線程都有獨(dú)立的棧并且是互相隔離的屯曹。

棧的大小

棧的大小可以受到幾個(gè)因素影響,一個(gè)是jvm參數(shù) -XSS,默認(rèn)值隨著虛擬機(jī)版本以及操作系統(tǒng)影響恶耽,從Oracle官網(wǎng)上我們可以找到:

In Java SE 6, the default on Sparc is 512k in the 32-bit VM, and 1024k in the 64-bit VM. On x86 Solaris/Linux it is 320k in the 32-bit VM and 1024k in the 64-bit VM.

我們可以認(rèn)為64位linux默認(rèn)是1m的樣子密任。

除了JVM設(shè)置,我們還可以在創(chuàng)建Thread的時(shí)候手工指定大型导蟆:

publicThread(ThreadGroup group, Runnable target, String name ,longstackSize)

棧的大小影響到了線程的最大數(shù)量浪讳,尤其在大流量的server中,我們很多時(shí)候的并發(fā)數(shù)受到的是線程數(shù)的限制涌萤,這時(shí)候需要了解限制在哪里淹遵。

第一個(gè)限制在操作系統(tǒng),以u(píng)buntu為例负溪,/proc/sys/kernel/threads-max 和/proc/sys/vm/max_map_count 定義了總的最大線程數(shù)(根據(jù)資料windows總的來說線程數(shù)會(huì)更少)和mmap這個(gè)system_call的最大數(shù)量(也就是從內(nèi)存方面限制了線程數(shù))

第二個(gè)限制自然是在JVM透揣,理論上我們能分配給線程的內(nèi)存除以單個(gè)線程占用的內(nèi)存就是最大線程數(shù)。所以說對(duì)Java進(jìn)程來講川抡,既然分配給了堆辐真,棧和靜態(tài)方法區(qū)(或叫永久代,perm區(qū))崖堤,我們可以大致認(rèn)為

線程數(shù) = (系統(tǒng)空閑內(nèi)存-堆內(nèi)存(-Xms, -Xmx)- perm區(qū)內(nèi)存(-XX:MaxPermSize)) / 線程棧大小(-Xss)

注意這只是幫助我們樹立一個(gè)概念侍咱,實(shí)際上還有許多因素影響。

棧的大小還影響到一個(gè)就是如果單個(gè)棧超過了這個(gè)大小密幔,就會(huì)拋出StackOverflowError楔脯,一般來說遞歸調(diào)用是常見的原因。

如何查看線程棧

使用命令 jstack 可以列出當(dāng)前pid對(duì)應(yīng)jvm的所有線程棧描述胯甩,描述主要包括了每個(gè)線程的狀態(tài)以及堆棧內(nèi)各棧幀的方法全限定名淤年,代碼位置。注意這只是為了可閱讀性蜡豹,并不是說棧里存著的就是這些字符串麸粮。

截取一段tomcat的jstack輸出(線程方面的知識(shí)可以參考另一篇拙作《Java多線程你只需要看這一篇就夠了》,本文不再贅述):

tomcat的jstack輸出片段

2.堆和垃圾收集

堆的結(jié)構(gòu)

對(duì)于大多數(shù)應(yīng)用來說镜廉,Java 堆(Java Heap)是Java 虛擬機(jī)所管理的內(nèi)存中最大的一塊弄诲。Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建娇唯。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例齐遵,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存。

分代的內(nèi)存管理

首先堆可以劃分為新生代和老年代塔插。

新生代

然后新生代又可以劃分為一個(gè)Eden區(qū)和兩個(gè)Survivor(幸存)區(qū)梗摇。

按照規(guī)定,新對(duì)象會(huì)首先分配在Eden中(如果對(duì)象過大想许,比如大數(shù)組伶授,將會(huì)直接放到老年代)断序。在GC中,Eden中的對(duì)象會(huì)被移動(dòng)到survivor中糜烹,直至對(duì)象滿足一定的年紀(jì)(定義為熬過minor GC的次數(shù))违诗,會(huì)被移動(dòng)到老年代。

新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數(shù) –XX:NewRatio 來指定 )

默認(rèn)的疮蹦,Eden : from : to = 8 : 1 : 1 ( 可以通過參數(shù) –XX:SurvivorRatio 來設(shè)定 )诸迟,即: Eden = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小愕乎。

然后講講垃圾收集

堆內(nèi)存和垃圾收集是密不可分的兩個(gè)主題阵苇,講垃圾收集的資料很多,但總的來說講的比較混亂感论,在這里我試圖從一個(gè)系統(tǒng)的視角展示垃圾收集慎玖。

垃圾收集的意義

垃圾收集的出現(xiàn)解放了C++中手工對(duì)內(nèi)存進(jìn)行管理的大量繁雜工作,手工malloc,free不僅增加程序復(fù)雜度笛粘,還增加了bug數(shù)量。

分代收集湿硝。即在新生代和老生代使用不同的收集方式薪前。在垃圾收集上,目標(biāo)主要有:加大系統(tǒng)吞吐量(減少總垃圾收集的資源消耗)关斜;減少最大STW(Stop-The-World)時(shí)間示括;減少總STW時(shí)間。不同的系統(tǒng)需要不同的達(dá)成目標(biāo)痢畜。而分代這一里程碑式的進(jìn)步首先極大減少了STW垛膝,然后可以自由組合來達(dá)到預(yù)定目標(biāo)。

可達(dá)性檢測(cè)

引用計(jì)數(shù):一種在jdk1.2之前被使用的垃圾收集算法丁稀,我們需要了解其思想吼拥。其主要思想就是維護(hù)一個(gè)counter,當(dāng)counter為0的時(shí)候認(rèn)為對(duì)象沒有引用线衫,可以被回收凿可。缺點(diǎn)是無法處理循環(huán)引用。目前iOS開發(fā)中的一個(gè)常見技術(shù)ARC(Automatic Reference Counting)也是采用類似的思路授账。在當(dāng)前的JVM中應(yīng)該是沒有被使用的枯跑。

根搜算法:思想是從gc root根據(jù)引用關(guān)系來遍歷整個(gè)堆并作標(biāo)記,稱之為mark白热,等會(huì)在具體收集器中介紹并行標(biāo)記和單線程標(biāo)記敛助。之后回收掉未被mark的對(duì)象,好處是解決了循環(huán)依賴這種『孤島效應(yīng)』屋确。這里的gc root主要指:

a.虛擬機(jī)棧(棧楨中的本地變量表)中的引用的對(duì)象

b.方法區(qū)中的類靜態(tài)屬性引用的對(duì)象

c.方法區(qū)中的常量引用的對(duì)象

d.本地方法棧中JNI的引用的對(duì)象

整理策略

復(fù)制:主要用在新生代的回收上纳击,通過from區(qū)和to區(qū)的來回拷貝续扔。需要特定的結(jié)構(gòu)(也就是Young區(qū)現(xiàn)在的結(jié)構(gòu))來支持,對(duì)于新生成的對(duì)象來說评疗,頻繁的去復(fù)制可以最快的找到那些不用的對(duì)象并回收掉空間测砂。所以說在JVM里YGC一定承擔(dān)了最大量的垃圾清除任務(wù)。

標(biāo)記清除/標(biāo)記整理:主要用在老生代回收上百匆,通過根搜的標(biāo)記然后清除或者整理掉不需要的對(duì)象砌些。

整理的過程

清除的過程

這里可以看到清除會(huì)產(chǎn)生碎片空間,對(duì)內(nèi)存利用不是很好加匈,但不是說整理優(yōu)于清除存璃,畢竟整理會(huì)更慢。比如CMSGC就是使用清除而不是整理的雕拼。

思考一下復(fù)制和標(biāo)記清除/整理的區(qū)別纵东,為什么新生代要用復(fù)制?因?yàn)閷?duì)新生代來講啥寇,一次垃圾收集要回收掉絕大部分對(duì)象偎球,我們通過冗余空間的辦法來加速整理過程(不冗余空間的整理操作要做swap,而冗余只需要做move)辑甜。同時(shí)可以記錄下每個(gè)對(duì)象的『年齡』從而優(yōu)化『晉升』操作使得中年對(duì)象不被錯(cuò)誤放到老年代衰絮。而反過來老年代偏穩(wěn)定,我們哪怕是用清除磷醋,也不會(huì)產(chǎn)生太多的碎片猫牡,并且整理的代價(jià)也并不會(huì)太大。

具體的垃圾收集器

新生代收集器:有Serial收集器邓线、ParNew收集器淌友、Parallel Scavenge收集器

老生代收集器:Serial Old收集器、Parallel Old收集器骇陈、CMS收集器震庭、G1收集器

垃圾收集器大家庭

以上所有的垃圾收集器都會(huì)發(fā)生STW,只不過FGC的STW時(shí)間更長(zhǎng)你雌。

幾款重點(diǎn)研究的垃圾收集器:

CMSGC:

CMS(Concurrent Mark-Sweep)是以犧牲吞吐量為代價(jià)來獲得最短回收停頓時(shí)間的垃圾回收器归薛。對(duì)于要求服務(wù)器響應(yīng)速度的應(yīng)用上,這種垃圾回收器非常適合匪蝙,因此我們又叫它低延遲垃圾收集器主籍。在啟動(dòng)JVM參數(shù)加上-XX:+UseConcMarkSweepGC ,這個(gè)參數(shù)表示對(duì)于老年代的回收采用CMS逛球,注意此時(shí)新生代默認(rèn)使用的是ParNew千元。CMS采用的基礎(chǔ)算法是:標(biāo)記—清除。

MSCGC vs CMSGC

和普通序列化整理(MSC)區(qū)別在于有三個(gè)mark階段(實(shí)際上還有個(gè)預(yù)清理過程颤绕,但對(duì)于解釋清楚CMSGC沒有幫助就忽略了)幸海。CMSGC的精髓在于因?yàn)樽龅搅瞬籗TW的情況下進(jìn)行mark祟身,我們得到了更短的總STW時(shí)間,代價(jià)是因?yàn)椴⑿衜ark產(chǎn)生了『臟數(shù)據(jù)』即在mark的同時(shí)又生成了需要mark的對(duì)象物独,我們必須再進(jìn)行一次STW袜硫,并收尾(remark)。

同時(shí)挡篓,我們要注意到得到更短的STW的同時(shí)婉陷,我們犧牲了系統(tǒng)吞吐量,CMSGC總吞吐量比ParOld要更低官研。

G1GC

作為最新的垃圾收集器秽澳,有可能在jdk9中成為默認(rèn)的垃圾收集器。

主要思路是將新生代老生代進(jìn)一步分為多個(gè)region戏羽,每次gc可以針對(duì)部分region而不是整個(gè)堆內(nèi)存担神。由此可以降低stw的單次最長(zhǎng)時(shí)間,代價(jià)是可能在總時(shí)間上會(huì)更高始花。

G1GC讓系統(tǒng)在整體吞吐量略降的情況下變得更加平滑穩(wěn)定妄讯。

為了比較ParOld,CMSGC和G1GC,附上從某篇博客上轉(zhuǎn)載的評(píng)測(cè)截圖:

靜態(tài)方法區(qū)

最后講一講靜態(tài)方法區(qū)酷宵,又稱為永久代(Perm Generation)亥贸。它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量忧吟、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)斩披。

常見的JVM配置包括:

-XX:MaxPermSize=512m

我們有時(shí)候會(huì)看到j(luò)ava進(jìn)程報(bào)一個(gè)錯(cuò)誤類似

Exceptioninthread"State Saver"java.lang.OutOfMemoryError: PermGen space

說明我們此時(shí)要調(diào)整配置了溜族,或者說代碼中有一些bug導(dǎo)致大量的perm區(qū)被占用,可能是用到了太多的靜態(tài)變量(一般懷疑map)或者說用到ASM框架導(dǎo)致產(chǎn)生了大量的類信息垦沉。

附錄

1.JVM的GC日志的主要參數(shù)

-XX:+PrintGC 輸出GC日志

-XX:+PrintGCDetails 輸出GC的詳細(xì)日志

-XX:+PrintGCTimeStamps 輸出GC的時(shí)間戳(以基準(zhǔn)時(shí)間的形式)

-XX:+PrintGCDateStamps 輸出GC的時(shí)間戳(以日期的形式煌抒,如 2013-05-04T21:53:59.234+0800)

-XX:+PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息

-XX:+PrintGCApplicationStoppedTime // 輸出GC造成應(yīng)用暫停的時(shí)間

-Xloggc:../logs/gc.log 日志文件的輸出路徑

-XX:+HeapDumpOnOutOfMemoryError //發(fā)生OOM的時(shí)候自動(dòng)dump堆棧方便分析

2.如何看垃圾收集策略

jmap -heap <pid>

3.如何實(shí)時(shí)看堆內(nèi)存的使用情況

jstat -gcutil [pid] [interval] //實(shí)時(shí)打印gc情況以及各代內(nèi)存占用比例

jmap -dump:format=b,file=f1 //dump內(nèi)存到二進(jìn)制文件

jmap -histo [pid]? //按占大小倒序列出內(nèi)存中的實(shí)例類型

4.關(guān)于晉升到老年代的條件

對(duì)象有兩種可能會(huì)進(jìn)入old區(qū):

存活對(duì)象過多。在s1和s2都已經(jīng)溢出了厕倍。如果從eden遷往survior區(qū)時(shí)寡壮,發(fā)現(xiàn)放不下,則直接進(jìn)入 old Gen

從eden到s區(qū)來回拷貝次數(shù)達(dá)到一定的數(shù)量讹弯,總沒有回收掉况既,進(jìn)入old區(qū)。(從eden到survior1遷到组民,引用持有中棒仍,s1中放不下新遷對(duì)象,則清理s1,存活對(duì)象臭胜,晉升入s2;再下次或繼續(xù)遷移莫其,就把s2中的癞尚。準(zhǔn)備說,可能是乱陡,這些個(gè)對(duì)象從s1<->s2來回拷貝一定次數(shù)后浇揩,會(huì)進(jìn)入old Gen)。這塊Servivor Space 調(diào)整合適的存活次數(shù) Threshold 通過-XX:MaxTenuringThreshold憨颠。但也只是一個(gè)建議胳徽,最終仍由虛擬機(jī)決定

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烙心,隨后出現(xiàn)的幾起案子膜廊,更是在濱河造成了極大的恐慌,老刑警劉巖淫茵,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爪瓜,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡匙瘪,警方通過查閱死者的電腦和手機(jī)铆铆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丹喻,“玉大人薄货,你說我怎么就攤上這事“郏” “怎么了谅猾?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鳍悠。 經(jīng)常有香客問我税娜,道長(zhǎng),這世上最難降的妖魔是什么藏研? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任敬矩,我火速辦了婚禮,結(jié)果婚禮上蠢挡,老公的妹妹穿的比我還像新娘弧岳。我一直安慰自己,他們只是感情好业踏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布禽炬。 她就那樣靜靜地躺著,像睡著了一般勤家。 火紅的嫁衣襯著肌膚如雪瞎抛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天却紧,我揣著相機(jī)與錄音桐臊,去河邊找鬼胎撤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛断凶,可吹牛的內(nèi)容都是我干的伤提。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼认烁,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼肿男!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起却嗡,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤舶沛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后窗价,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體如庭,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年撼港,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了坪它。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡帝牡,死狀恐怖往毡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情靶溜,我是刑警寧澤开瞭,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站罩息,受9級(jí)特大地震影響嗤详,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扣汪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一断楷、第九天 我趴在偏房一處隱蔽的房頂上張望锨匆。 院中可真熱鬧崭别,春花似錦、人聲如沸恐锣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽土榴。三九已至诀姚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間玷禽,已是汗流浹背赫段。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工呀打, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糯笙。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓贬丛,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親给涕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豺憔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1 CPU和內(nèi)存的交互 了解jvm內(nèi)存模型前,了解下cpu和計(jì)算機(jī)內(nèi)存的交互情況够庙」вΓ【因?yàn)镴ava虛擬機(jī)內(nèi)存模型定義...
    Garwer閱讀 366,912評(píng)論 54 549
  • JVM內(nèi)存模型Java虛擬機(jī)(Java Virtual Machine=JVM)的內(nèi)存空間分為五個(gè)部分,分別是: ...
    光劍書架上的書閱讀 2,509評(píng)論 2 26
  • 讓我們不厭其煩的從內(nèi)存模型開始說起:作為一般人需要了解到的耘眨,JVM的內(nèi)存區(qū)域可以被分為:線程棧昼榛,堆,靜態(tài)方法區(qū)(實(shí)...
    納達(dá)丶無忌閱讀 47,146評(píng)論 15 119
  • 大一的初雪 我的生日 大二的初雪 和你在一起 大三的初雪 新鄉(xiāng)與上海 安靜的待著 學(xué)會(huì)與自己相處 讀書 畫畫 (后...
    木惜錦年閱讀 112評(píng)論 0 1
  • 圖/網(wǎng)絡(luò) 我們一直渴望破繭成蝶,卻一直被“思維的墻”而束縛钥飞,看不到光亮莺掠。 直到讀完古典的《拆掉思維的墻》,才從混沌...
    清韻難敲閱讀 481評(píng)論 2 2