Java內(nèi)存機(jī)制講解

一 概述

眾所周知c語言是鼻祖.而讓c語言的特點(diǎn)之一就是指針.那在Java中是沒有指針這個(gè)概念的.但是沒有并不表示不存在,在Java中,每次new一個(gè)對(duì)象的時(shí)候,其實(shí)就是在內(nèi)存中開辟了一塊控件,對(duì)象的引用實(shí)際上就是指針.只不過java中把對(duì)內(nèi)存的操作交給了JVM.

c語言的指針讓你可以操作內(nèi)存,但是同時(shí)你也要去維護(hù)這個(gè)指針.而java中操作內(nèi)存的工作交給了JVM.所以一定程度上減輕了程序猿的負(fù)擔(dān).當(dāng)然若是在這種情況下出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出問題.如果沒有了解JVM是怎樣使用內(nèi)存的,將會(huì)導(dǎo)致異常排除變得非常困難.

二 JVM內(nèi)存分布


如圖.JVM的內(nèi)存主要包括兩個(gè)子系統(tǒng)和兩個(gè)組件.兩個(gè)子系統(tǒng)分別是CLASS LOADER(類加載器)和EXECUTION ENGINE(執(zhí)行引擎).兩個(gè)組件分別是RUNTIME DATA AREA(運(yùn)行數(shù)據(jù)區(qū)域)和NATIVE INTERFACE(本地接口庫)組件.

1.CLASS LOADER。java運(yùn)行的時(shí)候并不是直接運(yùn)行代碼,而是要通過類加載器把java class加載到JVM中然后運(yùn)行.負(fù)責(zé)加載的這部分就是CLASS LOADER.我們可以通過重寫ClassLoader來拓展程序的功能.例如:1)在執(zhí)行非置信代碼之前,自動(dòng)驗(yàn)證數(shù)字簽名.2)動(dòng)態(tài)地創(chuàng)建符合用戶特定需要的定制化構(gòu)建類.3)從特定的場(chǎng)所取得java class昨悼,例如數(shù)據(jù)庫中.4) 等等

2.EXECUTION ENGINE。執(zhí)行classes中的指令遂跟。任何JVM specification實(shí)現(xiàn)(JDK)的核心都是Execution engine,不同的JDK例如Sun 的JDK 和IBM的JDK好壞主要就取決于他們各自實(shí)現(xiàn)的Execution engine的好壞。

3.NATIVE INTERFACE .用作與其他語言編程的接口.可以通過這個(gè)組件調(diào)用其他語言的程序.

4.Runtime Data Area組件 這就是我們常說的java內(nèi)存了
? ? ?? 1顶燕、Heap (堆):一個(gè)Java虛擬實(shí)例中只存在一個(gè)堆空間,是Java內(nèi)存管理中最大的一塊.也是被所有的線程所共享的一塊.在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建.
??????? 堆內(nèi)存中存儲(chǔ)著基本上所有的對(duì)象.因?yàn)榧夹g(shù)的更新 現(xiàn)在也不是絕對(duì)的存放在堆內(nèi)存中,java中的gc也是主要對(duì)堆內(nèi)存進(jìn)行操作,所以堆內(nèi)存也可以稱為GC內(nèi)存.gc采用的算法是分代收集算法,所以在堆中又可以分為,年青代(Young)穗慕、年老代(Tenured).更細(xì)致的可以劃分為 Eden空間,From Survivor空間和To Survivor空間.不論怎么劃分.堆都是用來存放對(duì)象.
??? ? ? 根據(jù)Java虛擬機(jī)規(guī)范規(guī)定,java堆可以處于物理上不連續(xù)的內(nèi)存空間中(多條內(nèi)存條),只要邏輯連續(xù)即可.在創(chuàng)建堆的時(shí)候我們可以固定堆的大小,也可以使用可拓展的堆,如果堆中沒有足夠的內(nèi)存來分配對(duì)象就會(huì)報(bào)出OutOfMemory的錯(cuò)誤

? ? ? ? 2饿敲、Method Area(方法區(qū)域):Method Area和Heap一樣,也是被所有線程所共享的一塊,它是用于存儲(chǔ)已被虛擬機(jī)加載的類的信息,常量,靜態(tài)變量,即時(shí)編譯器編譯后的代碼等數(shù)據(jù),java對(duì)這個(gè)區(qū)域的管理比較輕松,除了和堆一樣可以選擇固定內(nèi)存和拓展以外,還可以對(duì)方法區(qū)選擇不進(jìn)行垃圾回收機(jī)制,相對(duì)而言,gc在這里觸發(fā)的概率相對(duì)較小,但也不意味著不會(huì)被回收,這個(gè)區(qū)域的回收工作主要是針對(duì)常量池的回收和類型的卸載.當(dāng)方法區(qū)無法滿足內(nèi)存分配的時(shí)候,會(huì)拋出OutOfMemory的錯(cuò)誤,

? ? ? ? 3、JavaStack(java的棧):Java Virtual Machine Stacks 是屬于線程私有的,他的生命周期和線程的相同.每個(gè)方法被執(zhí)行的時(shí)候都會(huì)在棧中創(chuàng)建一個(gè)棧幀.棧幀中包括局部變量表,操作棧,動(dòng)態(tài)鏈接,方法出口等.
局部變量表用于存儲(chǔ)方法的參數(shù)和局部變量.
操作棧,也成為"基于棧的執(zhí)行引擎",主要是運(yùn)行過程中的算數(shù)計(jì)算和調(diào)用其他方法參數(shù)的傳遞.
動(dòng)態(tài)鏈接是每個(gè)棧幀在執(zhí)行時(shí)常量池中都有一個(gè)引用,Class文件中會(huì)有大量的符號(hào)引用,字節(jié)碼中方法調(diào)用就是使用這些符號(hào)引用,這些符號(hào)引用在第一次加載的時(shí)候就轉(zhuǎn)換為直接引用的成為靜態(tài)解析(靜態(tài)方法),和每一次調(diào)用的時(shí)候才轉(zhuǎn)換為直接引用的成為動(dòng)態(tài)鏈接.
方法出口,當(dāng)方法運(yùn)行的時(shí)候只有兩種情況會(huì)退出方法,一種是異常退出,另一種是執(zhí)行到方法出口(return).當(dāng)方法執(zhí)行完可能進(jìn)行的操作是1.恢復(fù)上層方法的局部變量表和操作數(shù)棧.2.把返回值壓入調(diào)用者調(diào)用者棧幀的操作數(shù)棧.3.調(diào)整 PC 計(jì)數(shù)器的值以指向方法調(diào)用指令后面的一條指令.

? ? ? ? 4逛绵、Program Counter(程序計(jì)數(shù)器):每一個(gè)線程都有它自己的PC寄存器怀各,也是該線程啟動(dòng)時(shí)創(chuàng)建的倔韭。PC寄存器的內(nèi)容總是指向下一條將被執(zhí)行指令的餓地址,這里的地址可以是一個(gè)本地指針瓢对,也可以是在方法區(qū)中相對(duì)應(yīng)于該方法起始指令的偏移量寿酌。由于Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的,在任何一個(gè)確定的時(shí)刻沥曹,一個(gè)處理器(對(duì)于多核處理器來說是一個(gè)內(nèi)核)只會(huì)執(zhí)行一條線程中的指令份名。因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置妓美,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器僵腺,各條線程之間的計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)壶栋,我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存辰如。這塊內(nèi)存不會(huì)拋出OutOfMemory

? ? ? ? 5、Native method stack(本地方法棧):保存native方法進(jìn)入?yún)^(qū)域的地址

三 Java對(duì)象調(diào)用

介紹完java內(nèi)存的分布,現(xiàn)在來說說java的對(duì)象是怎樣的一個(gè)存在.最普通創(chuàng)建對(duì)象的方法是
Object object=new Object();
這里面包含了前面所說的三塊內(nèi)存的使用.Object object 這句話就是值在棧中保存了Object對(duì)象的引用,new Object();這段話則表示在堆內(nèi)存中開辟了一段內(nèi)存存儲(chǔ)Object的對(duì)象.Object() 這個(gè)方法是存在于方法區(qū)中.

在Java中reference類型引用對(duì)象有兩種主流的方式,一種是通過句柄訪問,一種是直接訪問.
1.句柄訪問會(huì)在堆內(nèi)存中劃分出一塊區(qū)域用于存儲(chǔ)句柄,reference只需要指向這個(gè)句柄,而句柄會(huì)指向?qū)ο蠛瓦@個(gè)對(duì)象的方法


2.直接引用 reference中直接存儲(chǔ)對(duì)象的地址


這兩種方式各有優(yōu)缺點(diǎn),采用句柄的形式,是在對(duì)象發(fā)生變化的時(shí)候(被回收了)只需要改變句柄的指向?qū)ο?不需要修改reference的指向.


采用直接指針訪問的時(shí)候,因?yàn)樯倭艘粚又羔樀闹赶?所以速度更快.

四 對(duì)象的回收

說完對(duì)象的調(diào)用,接下來就是對(duì)象的回收,在Java中g(shù)c的回收機(jī)制還是比較常見的.一下以堆內(nèi)存中的分代收集算法做舉例.

Sun的JVM Generational Collecting(垃圾回收)原理是這樣的:把對(duì)象分為年青代(Young)贵试、年老代(Tenured)琉兜、持久代(Perm),對(duì)不同生命周期的對(duì)象使用不同的算法毙玻。(基于對(duì)對(duì)象生命周期分析)

如上圖所示豌蟋,為Java堆中的各代分布。

1. Young(年輕代)

年輕代分三個(gè)區(qū)桑滩。一個(gè)Eden區(qū)梧疲,兩個(gè)Survivor區(qū)。大部分對(duì)象在Eden區(qū)中生成运准。當(dāng)Eden區(qū)滿時(shí)幌氮,還存活的對(duì)象將被復(fù)制到Survivor區(qū)(兩個(gè)中的一個(gè)),當(dāng)這個(gè)Survivor區(qū)滿時(shí)胁澳,此區(qū)的存活對(duì)象將被復(fù)制到另外一個(gè)Survivor區(qū)该互,當(dāng)這個(gè)Survivor去也滿了的時(shí)候,從第一個(gè)Survivor區(qū)復(fù)制過來的并且此時(shí)還存活的對(duì)象韭畸,將被復(fù)制年老區(qū)(Tenured宇智。需要注意,Survivor的兩個(gè)區(qū)是對(duì)稱的胰丁,沒先后關(guān)系普筹,所以同一個(gè)區(qū)中可能同時(shí)存在從Eden復(fù)制過來 對(duì)象,和從前一個(gè)Survivor復(fù)制過來的對(duì)象隘马,而復(fù)制到年老區(qū)的只有從第一個(gè)Survivor去過來的對(duì)象。而且妻顶,Survivor區(qū)總有一個(gè)是空的酸员。

2. Tenured(年老代)

年老代存放從年輕代存活的對(duì)象蜒车。一般來說年老代存放的都是生命期較長的對(duì)象。

3. Perm(持久代)

用于存放靜態(tài)文件幔嗦,如今Java類酿愧、方法等。持久代對(duì)垃圾回收沒有顯著影響邀泉,但是有些應(yīng)用可能動(dòng)態(tài)生成或者調(diào)用一些class嬉挡,例如hibernate等,在這種時(shí)候需要設(shè)置一個(gè)比較大的持久代空間來存放這些運(yùn)行過程中新增的類汇恤。持久代大小通過-XX:MaxPermSize=進(jìn)行設(shè)置庞钢。

舉個(gè)例子:當(dāng)在程序中生成對(duì)象時(shí),正常對(duì)象會(huì)在年輕代中分配空間因谎,如果是過大的對(duì)象也可能會(huì)直接在年老代生成(據(jù)觀測(cè)在運(yùn)行某程序時(shí)候每次會(huì)生成一個(gè)十兆的空間用收發(fā)消息基括,這部分內(nèi)存就會(huì)直接在年老代分配)。年輕代在空間被分配完的時(shí)候就會(huì)發(fā)起內(nèi)存回收财岔,大部分內(nèi)存會(huì)被回收风皿,一部分幸存的內(nèi)存會(huì)被拷貝至Survivor的from區(qū),經(jīng)過多次回收以后如果from區(qū)內(nèi)存也分配完畢匠璧,就會(huì)也發(fā)生內(nèi)存回收然后將剩余的對(duì)象拷貝至to區(qū)桐款。等到to區(qū)也滿的時(shí)候,就會(huì)再次發(fā)生內(nèi)存回收然后把幸存的對(duì)象拷貝至年老區(qū)夷恍。

通常我們說的JVM內(nèi)存回收總是在指堆內(nèi)存回收魔眨,確實(shí)只有堆中的內(nèi)容是動(dòng)態(tài)申請(qǐng)分配的,所以以上對(duì)象的年輕代和年老代都是指的JVM的Heap空間裁厅,而持久代則是之前提到的Method Area冰沙,不屬于Heap。

了解完這些之后执虹,以下的轉(zhuǎn)載一熱衷于鉆研技術(shù)的哥們Richen Wang關(guān)于內(nèi)存管理的一些建議——

1拓挥、手動(dòng)將生成的無用對(duì)象,中間對(duì)象置為null袋励,加快內(nèi)存回收侥啤。

2、對(duì)象池技術(shù) 如果生成的對(duì)象是可重用的對(duì)象茬故,只是其中的屬性不同時(shí)盖灸,可以考慮采用對(duì)象池來較少對(duì)象的生成。如果有空閑的對(duì)象就從對(duì)象池中取出使用磺芭,沒有再生成新的對(duì)象赁炎,大大提高了對(duì)象的復(fù)用率。

3钾腺、JVM調(diào)優(yōu) 通過配置JVM的參數(shù)來提高垃圾回收的速度徙垫,如果在沒有出現(xiàn)內(nèi)存泄露且上面兩種辦法都不能保證內(nèi)存的回收時(shí)讥裤,可以考慮采用JVM調(diào)優(yōu)的方式來解決,不過一定要經(jīng)過實(shí)體機(jī)的長期測(cè)試姻报,因?yàn)椴煌膮?shù)可能引起不同的效果己英。如-Xnoclassgc參數(shù)等。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吴旋,一起剝皮案震驚了整個(gè)濱河市损肛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荣瑟,老刑警劉巖治拿,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異褂傀,居然都是意外死亡忍啤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門仙辟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來同波,“玉大人,你說我怎么就攤上這事叠国∥撮荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵粟焊,是天一觀的道長冤狡。 經(jīng)常有香客問我,道長项棠,這世上最難降的妖魔是什么悲雳? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮香追,結(jié)果婚禮上合瓢,老公的妹妹穿的比我還像新娘。我一直安慰自己透典,他們只是感情好晴楔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著峭咒,像睡著了一般税弃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上凑队,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天则果,我揣著相機(jī)與錄音,去河邊找鬼。 笑死短条,一個(gè)胖子當(dāng)著我的面吹牛导匣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茸时,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赋访!你這毒婦竟也來了可都?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤蚓耽,失蹤者是張志新(化名)和其女友劉穎渠牲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步悠,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡签杈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鼎兽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片答姥。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖谚咬,靈堂內(nèi)的尸體忽然破棺而出鹦付,到底是詐尸還是另有隱情,我是刑警寧澤择卦,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布敲长,位于F島的核電站,受9級(jí)特大地震影響秉继,放射性物質(zhì)發(fā)生泄漏祈噪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一尚辑、第九天 我趴在偏房一處隱蔽的房頂上張望辑鲤。 院中可真熱鬧,春花似錦腌巾、人聲如沸遂填。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吓坚。三九已至,卻和暖如春灯荧,著一層夾襖步出監(jiān)牢的瞬間礁击,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哆窿,地道東北人链烈。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像挚躯,于是被迫代替她去往敵國和親强衡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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