介紹JVM中7個(gè)區(qū)域器一,然后把每個(gè)區(qū)域可能造成內(nèi)存的溢出的情況說(shuō)明
程序計(jì)數(shù)器:看做當(dāng)前線程所執(zhí)行的字節(jié)碼行號(hào)指示器者填。是線程私有的內(nèi)存探越,且唯一一塊不報(bào)OutOfMemoryError異常雁比。Java虛擬機(jī)棧:用于描述java方法的內(nèi)存模型:每個(gè)方法被執(zhí)行時(shí)都會(huì)同時(shí)創(chuàng)建一個(gè)棧幀用于存儲(chǔ)局部變量表蔚袍,操作數(shù)棧,動(dòng)態(tài)鏈接奸柬,方法出口等信息生年。每一個(gè)方法被調(diào)用直至執(zhí)行完成的過(guò)程就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中從入棧到出棧的過(guò)程。如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度就報(bào)StackOverflowError, 如果虛擬機(jī)椑龋可以動(dòng)態(tài)擴(kuò)展抱婉,當(dāng)拓展時(shí)無(wú)法申請(qǐng)到足夠的內(nèi)存會(huì)拋出OutOfMemoryError. 是線程私有的。本地方法棧:與虛擬機(jī)棧相似桌粉,不同的在于它是為虛擬機(jī)使用到的Native方法服務(wù)的蒸绩。會(huì)拋出StackOverflowError和OutOfMemoryError。是線程私有的番甩。Java堆:是所有線程共享的一塊內(nèi)存侵贵,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例缘薛,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存窍育。如果堆上沒有內(nèi)存完成實(shí)例的分配就會(huì)報(bào)OutOfMemoryError.方法區(qū)(永久代):用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量宴胧、靜態(tài)變量漱抓、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。當(dāng)方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí)恕齐,會(huì)拋出OutOfMemoryError乞娄。是共享內(nèi)存。運(yùn)行時(shí)常量池:用于存放編譯器生成的各種字面量和符號(hào)引用显歧,是方法區(qū)的一部分仪或。無(wú)法申請(qǐng)內(nèi)存時(shí)拋出OutOfMemoryError。直接內(nèi)存:不是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)的一部分士骤,也不是java虛擬機(jī)規(guī)范中定義的區(qū)域范删,是計(jì)算機(jī)直接的內(nèi)存空間。這部分也被頻繁使用拷肌,如JAVA NIO的引入基于通道和緩存區(qū)的I/O使用native函數(shù)直接分配堆外內(nèi)存到旦。如果內(nèi)存不足會(huì)報(bào)OutOfMemoryError。
GC的兩種判定方法:引用計(jì)數(shù)與根搜索算法巨缘。引用計(jì)數(shù): 給對(duì)象添加一個(gè)引用計(jì)數(shù)器添忘,每當(dāng)有一個(gè)地方引用該對(duì)象時(shí),計(jì)數(shù)器值加1若锁,當(dāng)引用失效時(shí)搁骑,計(jì)數(shù)器值減1,。任何時(shí)候計(jì)數(shù)器都為0的對(duì)象就是不可能再被使用的。它很難解決對(duì)象之間相互循環(huán)引用問題靶病。根搜索算法(GC Roots Traceing):通過(guò)一系列名為“GC Roots”的對(duì)象作為起點(diǎn)会通,從這些節(jié)點(diǎn)開始向下搜索口予,搜索走過(guò)的路徑成為引用鏈娄周,當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí),則證明此對(duì)象不可用沪停。 GC Roots對(duì)象一般是:虛擬機(jī)棧中的引用對(duì)象煤辨,方法區(qū)中類靜態(tài)屬性引用的對(duì)象,方法區(qū)常量引用的對(duì)象等木张。
Java中的四種引用
強(qiáng)引用:程序代碼中的普通引用众辨。如Object obj = new Object(),只要強(qiáng)引用存在,垃圾回收器就不會(huì)回收舷礼。軟引用:描述一些有用但并非必須的對(duì)象鹃彻。對(duì)于軟引用關(guān)聯(lián)的對(duì)象在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前,將會(huì)把這些對(duì)象列進(jìn)回收范圍之中進(jìn)行第二次回收妻献。SoftRefence弱引用:描述非必須對(duì)象蛛株,比軟引用弱一些。被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前育拨。無(wú)論當(dāng)前內(nèi)存是否足夠谨履,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。WeakRefence虛引用:最弱的引用熬丧,不管是否有虛引用存在笋粟,完全不會(huì)對(duì)對(duì)象生存時(shí)間構(gòu)成影響,也無(wú)法通過(guò)虛引用來(lái)取得一個(gè)對(duì)象實(shí)例析蝴。唯一目的是希望能夠在這個(gè)對(duì)象被垃圾回收器之前收到系統(tǒng)通知害捕。PhantomReference
對(duì)象創(chuàng)建方法,對(duì)象的內(nèi)存分配闷畸,對(duì)象的訪問定位尝盼。
Object obj = new Object(); obj 保存在java棧中的局部變量表里,作為一個(gè)引用數(shù)據(jù)出現(xiàn)腾啥。 New Object()會(huì)在java堆上分配一塊存儲(chǔ)Object類型實(shí)例的所有數(shù)值的結(jié)構(gòu)化內(nèi)存东涡,根據(jù)類型以及虛擬機(jī)實(shí)現(xiàn)的對(duì)象內(nèi)存布局不同。這塊內(nèi)存是不固定的倘待。 對(duì)象訪問方式有兩種:句柄和直接指針:句柄:在java堆中會(huì)劃分出一塊內(nèi)存作為句柄池疮跑,reference中存儲(chǔ)的對(duì)象是句柄地址。而句柄中包含對(duì)象實(shí)例數(shù)據(jù)和類型數(shù)據(jù)各自的具體地址信息凸舵。最大的好處是如果對(duì)象地址發(fā)生變化不需要改變r(jià)eference的值祖娘,只需要改變句柄中實(shí)例數(shù)據(jù)指針。直接指針訪問:reference直接存儲(chǔ)對(duì)象的地址啊奄,最大的好處是速度更快渐苏。
內(nèi)存溢出和內(nèi)存泄漏
內(nèi)存溢出:通俗理解就是內(nèi)存不夠掀潮,程序所需要的內(nèi)存遠(yuǎn)遠(yuǎn)超出了你虛擬機(jī)分配的內(nèi)存大小,就叫內(nèi)存溢出內(nèi)存泄露:內(nèi)存泄漏也稱作“存儲(chǔ)滲漏”琼富,用動(dòng)態(tài)存儲(chǔ)分配函數(shù)動(dòng)態(tài)開辟的空間仪吧,在使用完畢后未釋放,結(jié)果導(dǎo)致一直占據(jù)該內(nèi)存單元鞠眉。直到程序結(jié)束薯鼠。(其實(shí)說(shuō)白了就是該內(nèi)存空間使用完畢之后未回收)即所謂內(nèi)存泄漏
內(nèi)存溢出了怎么辦
通過(guò)內(nèi)存映像工具如jhat,jconsole等對(duì)dump出來(lái)的堆轉(zhuǎn)存儲(chǔ)快照進(jìn)行分析,重點(diǎn)是確認(rèn)內(nèi)存是出現(xiàn)內(nèi)存泄露還是內(nèi)存溢出械蹋。 如果是內(nèi)存泄露進(jìn)一步使用工具查看泄露的對(duì)象到GC Roots的引用鏈出皇。于是就能找到泄露對(duì)象是通過(guò)怎樣的路徑與GC Roots相關(guān)聯(lián)并導(dǎo)致垃圾收集器無(wú)法自動(dòng)回收它們。掌握泄露對(duì)象的信息哗戈,以及GC Roots引用鏈的信息郊艘,就可以比較準(zhǔn)確定位泄露代碼的位置。 如果不存在**內(nèi)存泄露唯咬,那就需要通過(guò)jinfo纱注,Jconsole等工具分析java堆參數(shù)與機(jī)器物理內(nèi)存對(duì)比是否還可以調(diào)大,從代碼上檢查是否存在某些對(duì)象生命周期過(guò)長(zhǎng)副渴,持有狀態(tài)過(guò)長(zhǎng)的情況奈附,嘗試減少程序的運(yùn)行消耗。
Java 中有內(nèi)存泄露嗎煮剧?
有斥滤,Java中,造成內(nèi)存泄露的原因有很多種勉盅。典型的例子是長(zhǎng)生命周期的對(duì)象持有短生命周期對(duì)象的引用就很可能發(fā)生內(nèi)存泄露佑颇,盡管短生命周期對(duì)象已經(jīng)不再需要,但是因?yàn)殚L(zhǎng)生命周期對(duì)象持有它的引用而導(dǎo)致不能被回收草娜,這就是java中內(nèi)存泄露的發(fā)生場(chǎng)景挑胸,通俗地說(shuō),就是程序員可能創(chuàng)建了一個(gè)對(duì)象宰闰,以后一直不再使用這個(gè)對(duì)象茬贵,這個(gè)對(duì)象卻一直被引用,即這個(gè)對(duì)象無(wú)用但是卻無(wú)法被垃圾回收器回收的移袍,這就是java中可能出現(xiàn)內(nèi)存泄露的情況解藻,例如,緩存系統(tǒng)葡盗,我們加載了一個(gè)對(duì)象放在緩存中(例如放在一個(gè)全局map對(duì)象中)螟左,然后一直不再使用它,這個(gè)對(duì)象一直被緩存引用,但卻不再被使用胶背。 檢查java中的內(nèi)存泄露巷嚣,一定要讓程序?qū)⒏鞣N分支情況都完整執(zhí)行到程序結(jié)束,然后看某個(gè)對(duì)象是否被使用過(guò)钳吟,如果沒有廷粒,則才能判定這個(gè)對(duì)象屬于內(nèi)存泄露。(采用什么工具砸抛?)如果一個(gè)外部類的實(shí)例對(duì)象的方法返回了一個(gè)內(nèi)部類的實(shí)例對(duì)象评雌,這個(gè)內(nèi)部類對(duì)象被長(zhǎng)期引用了树枫,即使那個(gè)外部類實(shí)例對(duì)象不再被使用直焙,但由于內(nèi)部類持久外部類的實(shí)例對(duì)象,這個(gè)外部類對(duì)象將不會(huì)被垃圾回收砂轻,這也會(huì)造成內(nèi)存泄露奔誓。http://www.mamicode.com/info-detail-504269.html
什么時(shí)候會(huì)發(fā)生jvm堆(持久區(qū))內(nèi)存溢出
簡(jiǎn)單的來(lái)說(shuō) java的堆內(nèi)存分為兩塊:permantspace(持久代) 和 heap space。 持久帶中主要存放用于存放靜態(tài)類型數(shù)據(jù)搔涝,如 Java Class, Method 等厨喂, 與垃圾收集器要收集的Java對(duì)象關(guān)系不大。 而heapspace分為年輕代和年老代 年輕代的垃圾回收叫 Young GC庄呈, 年老代的垃圾回收叫 Full GC蜕煌。 在年輕代中經(jīng)歷了N次(可配置)垃圾回收后仍然存活的對(duì)象,就會(huì)被復(fù)制到年老代中诬留。因此斜纪,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象 年老代溢出原因有 循環(huán)上萬(wàn)次的字符串處理、創(chuàng)建上千萬(wàn)個(gè)對(duì)象文兑、在一段代碼內(nèi)申請(qǐng)上百M(fèi)甚至上G的內(nèi)存 持久代溢出原因動(dòng)態(tài)加載了大量Java類而導(dǎo)致溢出盒刚,以及生產(chǎn)大量的常量。永久代內(nèi)存泄露: 以一個(gè)部署到應(yīng)用程序服務(wù)器的Java web程序來(lái)說(shuō)绿贞,當(dāng)該應(yīng)用程序被卸載的時(shí)候因块,你的EAR/WAR包中的所有類都將變得無(wú)用。只要應(yīng)用程序服務(wù)器還活著籍铁,JVM將繼續(xù)運(yùn)行涡上,但是一大堆的類定義將不再使用,理應(yīng)將它們從永久代(PermGen)中移除拒名。如果不移除的話吩愧,我們?cè)谟谰么≒ermGen)區(qū)域就會(huì)有內(nèi)存泄漏。
堆里面的分區(qū):Eden靡狞,survival from to耻警,老年代,各自的特點(diǎn)。
新生代:朝生夕死 老年代一般是放對(duì)象和長(zhǎng)期存活對(duì)象甘穿。當(dāng)一個(gè)對(duì)象分配的內(nèi)存空間大于某個(gè)閾值時(shí)或則年齡增加到一定程度(默認(rèn)15歲)就進(jìn)入老年代腮恩。
OOM你遇到過(guò)哪些情況
java.lang.OutOfMemoryError: Java heap space ------>java堆內(nèi)存溢出,此種情況最常見温兼,一般由于內(nèi)存泄露或者堆的大小設(shè)置不當(dāng)引起秸滴。 java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法區(qū)溢出了募判,一般出現(xiàn)于大量Class或者jsp頁(yè)面荡含,或者采用cglib等反射機(jī)制的情況,因?yàn)樯鲜銮闆r會(huì)產(chǎn)生大量的Class信息存儲(chǔ)于方法區(qū)届垫。 java.lang.StackOverflowError ------> 不會(huì)拋OOM error释液,但也是比較常見的Java內(nèi)存溢出。JAVA虛擬機(jī)棧溢出装处,一般是由于程序中存在死循環(huán)或者深度遞歸調(diào)用造成的误债,棧大小設(shè)置太小也會(huì)出現(xiàn)此種溢出⊥ǎ可以通過(guò)虛擬機(jī)參數(shù)-Xss來(lái)設(shè)置棧的大小寝蹈。
GC的三種收集方法:標(biāo)記清除、標(biāo)記整理登淘、復(fù)制算法的原理與特點(diǎn)箫老,分別用在什么地方,如果讓你優(yōu)化收集方法黔州,有什么思路耍鬓?
標(biāo)記清理:首先標(biāo)記所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象辩撑,它的標(biāo)記的對(duì)象界斜。缺點(diǎn)是效率低,且存在內(nèi)存碎片合冀。主要用于老生代垃圾回收各薇。復(fù)制算法:將內(nèi)存按容量劃分為大小相等的一塊,每次只用其中一塊君躺。當(dāng)內(nèi)存用完了峭判,將還存活的對(duì)象復(fù)制到另一塊內(nèi)存,然后把已使用過(guò)的內(nèi)存空間一次清理掉棕叫。實(shí)現(xiàn)簡(jiǎn)單林螃,高效。一般用于新生代俺泣。一般是將內(nèi)存分為一塊較大的Eden空間和兩塊較小的Survivor空間疗认。HotSpot虛擬機(jī)默認(rèn)比例是8:1,完残。每次使用Eden和一塊Survivor,當(dāng)回收時(shí)將這兩塊內(nèi)存中還存活的對(duì)象復(fù)制到Survivor然后清理掉剛才Eden和Survivor的空間横漏。如果復(fù)制過(guò)程內(nèi)存不夠使用則向老年代分配擔(dān)保谨设。標(biāo)記整理:首先標(biāo)記所有需要回收的對(duì)象,在標(biāo)記完成后讓所有存活的對(duì)象都向一端移動(dòng)缎浇,然后直接清理掉端邊界意外的內(nèi)存扎拣。用于老年代。分代收集算法:根據(jù)對(duì)象的生存周期將內(nèi)存劃分為新生代和老年代素跺,根據(jù)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
GC收集器有哪些二蓝?CMS收集器與G1收集器的特點(diǎn)。
Serial: 單線程收集器指厌,只會(huì)使用一個(gè)CPU或一條收集器線程去完成刊愚,垃圾回收工作,更重要的是在進(jìn)行垃圾回收時(shí)仑乌,必須暫停其他所有的工作線程百拓。(Stop the world)。簡(jiǎn)單高效晰甚,用于新生代。ParNew: 是Serial收集器的多線程版本决帖,垃圾回收時(shí)采用多線程方式進(jìn)行回收厕九。默認(rèn)情況下使用的線程數(shù)是cpu數(shù)量。除了serial收集器地回,目前只有它能和CMS收集器配合工作扁远。Parallel Scavenge: 使用復(fù)制算法收集器,也是一個(gè)并行的多線程收集器刻像。Parallel Scavenge收集器與其他收集器關(guān)注點(diǎn)不同畅买,其它收集器主要關(guān)注縮短垃圾回收時(shí)用戶線程的停頓時(shí)間。而它關(guān)心吞吐量细睡,即運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)谷羞。停頓時(shí)間越短越適合需要與用戶交互的程序,高吞吐量則可以最高效率的利用CPU時(shí)間溜徙。Serial Old: 老年代湃缎,單線程收集器,使用標(biāo)記整理算法蠢壹。主要有兩個(gè)用途嗓违,一是和Parallel Scavenge 收集器配合使用,二是作為CMS的后備方案在并發(fā)收集器發(fā)生Concurrent Mode Failure時(shí)候使用图贸。Parallel Old:并行的老年代版本收集器蹂季,使用標(biāo)記整理算法冕广。主要與Parallel Scavenge配合使用。CMS:是以獲得最短回收停頓時(shí)間為目標(biāo)的收集器偿洁,使用標(biāo)記清除算法佳窑。整個(gè)過(guò)程包括4個(gè):初始標(biāo)記: 標(biāo)記Gc ROOTS能直接關(guān)聯(lián)到的對(duì)象并發(fā)標(biāo)記:進(jìn)行Roots Traceing的過(guò)程重新標(biāo)記:修正并發(fā)標(biāo)記期間因用戶繼續(xù)工作導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)并發(fā)清除: 初始標(biāo)記和重新標(biāo)記需要stop the world. 并發(fā)標(biāo)記和并發(fā)清除過(guò)程用戶線程和收集器線程可以并行執(zhí)行。G1(Garbage First):基于標(biāo)記-整理算法的收集器,不會(huì)產(chǎn)生空間碎片.它可以精確控制停頓,能夠讓使用者明確指定一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段內(nèi),消耗集上的時(shí)間不超過(guò)N秒.是不犧牲吞吐量的前提下完成低停頓的.G1將整個(gè)java堆(新生和老生)劃分為大小相同的區(qū),并跟蹤這些區(qū)上發(fā)生的變化.在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)允許的收集時(shí)間優(yōu)先回收垃圾最多的區(qū)域.
Minor GC與Full GC分別在什么時(shí)候發(fā)生父能?
FullGC 一般是發(fā)生在老年代的GC神凑,出現(xiàn)一個(gè)FullGC經(jīng)常會(huì)伴隨至少一次的Minor GC。速度比MinorGC慢10倍以上何吝。FULL GC發(fā)生的情況: 1) 老年代空間不足 老年代空間只有在新生代對(duì)象轉(zhuǎn)入及創(chuàng)建為大對(duì)象溉委、大數(shù)組時(shí)才會(huì)出現(xiàn)不足的現(xiàn)象,當(dāng)執(zhí)行Full GC后空間仍然不足爱榕,則拋出如下錯(cuò)誤:java.lang.OutOfMemoryError: Java heap space . 措施:為避免以上兩種狀況引起的FullGC瓣喊,調(diào)優(yōu)時(shí)應(yīng)盡量做到讓對(duì)象在Minor GC階段被回收、讓對(duì)象在新生代多存活一段時(shí)間及不要?jiǎng)?chuàng)建過(guò)大的對(duì)象及數(shù)組 2) Permanet Generation(方法區(qū)或永久代)空間滿 PermanetGeneration中存放的為一些class的信息等黔酥,當(dāng)系統(tǒng)中要加載的類藻三、反射的類和調(diào)用的方法較多時(shí),Permanet Generation可能會(huì)被占滿跪者,在未配置為采用CMS GC的情況下會(huì)執(zhí)行Full GC棵帽。如果經(jīng)過(guò)Full GC仍然回收不了,那么JVM會(huì)拋出如下錯(cuò)誤信息: java.lang.OutOfMemoryError: PermGen space 措施:為避免Perm Gen占滿造成Full GC現(xiàn)象渣玲,可采用的方法為增大Perm Gen空間或轉(zhuǎn)為使用CMS GC逗概。 3) CMS GC時(shí)出現(xiàn)promotion failed和concurrent mode failure 對(duì)于采用CMS進(jìn)行老年代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure兩種狀況忘衍,當(dāng)這兩種狀況出現(xiàn)時(shí)可能會(huì)觸發(fā)Full GC逾苫。 promotion failed是在進(jìn)行Minor GC時(shí),survivor space放不下枚钓、對(duì)象只能放入老年代铅搓,而此時(shí)老年代也放不下造成的; concurrent mode failure: CMS在執(zhí)行垃圾回收時(shí)需要一部分的內(nèi)存空間并且此刻用戶程序也在運(yùn)行需要預(yù)留一部分內(nèi)存給用戶程序搀捷,如果預(yù)留的內(nèi)存無(wú)法滿足程序需求就出現(xiàn)一次"Concurrent mod failure",并觸發(fā)一次Full GC星掰。 應(yīng)對(duì)措施為:增大survivor space、老年代空間或調(diào)低觸發(fā)并發(fā)GC的比率指煎, 4) 統(tǒng)計(jì)得到的Minor GC晉升到舊生代的平均大小大于舊生代的剩余空間 Hotspot為了避免由于新生代對(duì)象晉升到舊生代導(dǎo)致舊生代空間不足的現(xiàn)象蹋偏,在進(jìn)行Minor GC時(shí),做了一個(gè)判斷至壤,如果之前統(tǒng)計(jì)所得到的Minor GC晉升到舊生代的平均大小大于舊生代的剩余空間威始,那么就直接觸發(fā)Full GC。如果小于并且不允許擔(dān)保失敗也會(huì)發(fā)生一次Full GC
MinorGC MinorGC 指發(fā)生在新生代的垃圾收集動(dòng)作像街,非常頻繁黎棠,回收速度也快晋渺。一般發(fā)生在新生代空間不足時(shí),另外一個(gè)FullGC經(jīng)常會(huì)伴隨至少一次的Minor GC. 當(dāng)虛擬檢測(cè)晉升到到老年代的平均大小是否小于老年代剩余空間大小,如果小于并且允許擔(dān)保失敗,則執(zhí)行Minor GC.http://zhidao.baidu.com/link?url=hxwfrDGb87nYAAaKh6kQerv45RzwFkGWOcvl9wbaKVwb8rjdiQNgxLy19ga_A0W8ozisKwCkrSlMjISwB1PeDcoXN9Z7qf12CdEm19VxiuO
幾種常用的內(nèi)存調(diào)試工具:jmap、jstack脓斩、jconsole木西。
(如何用工具分析jvm狀態(tài))jps: 列出正在虛擬機(jī)運(yùn)行的虛擬機(jī)進(jìn)程,并顯示虛擬機(jī)執(zhí)行主類的名稱随静,以及這些進(jìn)程的本地虛擬機(jī)的唯一ID八千。jstat: 監(jiān)視虛擬機(jī)各種運(yùn)行狀態(tài)信息的命令×敲停可以顯示本地或遠(yuǎn)程虛擬機(jī)進(jìn)程中類裝載,垃圾收集恋捆,JIT編譯,內(nèi)存等數(shù)據(jù)。jinof: 實(shí)時(shí)查看和調(diào)整虛擬機(jī)的各項(xiàng)參數(shù)重绷。jmap: 生成堆轉(zhuǎn)存儲(chǔ)快照沸停,查詢fianlize執(zhí)行隊(duì)列、java堆和永生代詳細(xì)信息昭卓,如空間使用率愤钾,當(dāng)前用的是那種收集器。Jhat: 和jmap搭配使用候醒,來(lái)分析jmap生成的堆轉(zhuǎn)存儲(chǔ)快照能颁。內(nèi)置一個(gè)微型的HTTP/HTML服務(wù)器,生成dump文件的分析結(jié)果后火焰,可以通過(guò)瀏覽器查看劲装。jstack:用于生成當(dāng)前時(shí)刻線程快照.線程快照是當(dāng)前虛擬機(jī)內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合.生成線程快照的主要目的是為了定位線程長(zhǎng)時(shí)間停頓的原因.如死鎖,死循環(huán),請(qǐng)求外部資源導(dǎo)致的長(zhǎng)時(shí)間等待.JConsole:可視化監(jiān)視和管理工具,幾乎包括以上工具的所有功能VisualVM
GC 是什么?為什么要有 GC
答:GC 是垃圾收集的意思昌简,內(nèi)存處理是編程人員容易出現(xiàn)問題的地方,忘記或者錯(cuò)誤的內(nèi)存回收會(huì)導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰绒怨,Java 提供的 GC 功能可以自動(dòng)監(jiān)測(cè)對(duì)象是否超過(guò)作用域從而達(dá)到自動(dòng)回收內(nèi)存的目的纯赎,Java 語(yǔ)言沒有提供釋放已分配內(nèi)存的顯示操作方法。Java 程序員不用擔(dān)心內(nèi)存管理南蹂,因?yàn)槔占鲿?huì)自動(dòng)進(jìn)行管理犬金。要請(qǐng)求垃圾收集,可以調(diào)用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() 六剥,但 JVM 可以屏蔽掉顯示的垃圾回收調(diào)用晚顷。
垃圾回收可以有效的防止內(nèi)存泄露,有效的使用可以使用的內(nèi)存疗疟。垃圾回收器通常是作為一個(gè)單獨(dú)的低優(yōu)先級(jí)的線程運(yùn)行该默,不可預(yù)知的情況下對(duì)內(nèi)存堆中已經(jīng)死亡的或者長(zhǎng)時(shí)間沒有使用的對(duì)象進(jìn)行清除和回收,程序員不能實(shí)時(shí)的調(diào)用垃圾回收器對(duì)某個(gè)對(duì)象或所有對(duì)象進(jìn)行垃圾回收策彤。在 Java 誕生初期栓袖,垃圾回收是 Java 最大的亮點(diǎn)之一匣摘,因?yàn)榉?wù)器端的編程需要有效的防止內(nèi)存泄露問題,然而時(shí)過(guò)境遷裹刮,如今 Java 的垃圾回收機(jī)制已經(jīng)成為被詬病的東西音榜。移動(dòng)智能終端用戶通常覺得 iOS 的系統(tǒng)比 Android 系統(tǒng)有更好的用戶體驗(yàn),其中一個(gè)深層次的原因就在于 Android 系統(tǒng)中垃圾回收的不可預(yù)知性捧弃。
JVM 加載 class 文件的原理機(jī)制
JVM 中類的裝載是由類加載器(ClassLoader) 和它的子類來(lái)實(shí)現(xiàn)的赠叼,Java 中的類加載器是一個(gè)重要的 Java 運(yùn)行時(shí)系統(tǒng)組件,它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類文件中的類违霞。 由于 Java 的跨平臺(tái)性嘴办,經(jīng)過(guò)編譯的 Java 源程序并不是一個(gè)可執(zhí)行程序,而是一個(gè)或多個(gè)類文件葛家。當(dāng) Java 程序需要使用某個(gè)類時(shí)户辞,JVM 會(huì)確保這個(gè)類已經(jīng)被加載、連接(驗(yàn)證癞谒、準(zhǔn)備和解析)和初始化底燎。類的加載是指把類的 .class 文件中的數(shù)據(jù)讀入到內(nèi)存中,通常是創(chuàng)建一個(gè)字節(jié)數(shù)組讀入 .class 文件弹砚,然后產(chǎn)生與所加載類對(duì)應(yīng)的 Class 對(duì)象双仍。加載完成后,Class 對(duì)象還不完整桌吃,所以此時(shí)的類還不可用朱沃。當(dāng)類被加載后就進(jìn)入連接階段,這一階段包括驗(yàn)證茅诱、準(zhǔn)備(為靜態(tài)變量分配內(nèi)存并設(shè)置默認(rèn)的初始值)和解析(將符號(hào)引用替換為直接引用)三個(gè)步驟逗物。最后 JVM 對(duì)類進(jìn)行初始化,包括:1. 如果類存在直接的父類并且這個(gè)類還沒有被初始化瑟俭,那么就先初始化父類翎卓;2. 如果類中存在初始化語(yǔ)句,就依次執(zhí)行這些初始化語(yǔ)句摆寄。 類的加載是由類加載器完成的失暴,類加載器包括:根加載器(BootStrap)、擴(kuò)展加載器(Extension)微饥、系統(tǒng)加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)逗扒。從JDK 1.2開始,類加載過(guò)程采取了父親委托機(jī)制(PDM)欠橘。PDM 更好的保證了 Java 平臺(tái)的安全性矩肩,在該機(jī)制中,JVM 自帶的 Bootstrap 是根加載器简软,其他的加載器都有且僅有一個(gè)父類加載器蛮拔。類的加載首先請(qǐng)求父類加載器加載述暂,父類加載器無(wú)能為力時(shí)才由其子類加載器自行加載。JVM 不會(huì)向 Java 程序提供對(duì) Bootstrap 的引用建炫。下面是關(guān)于幾個(gè)類加載器的說(shuō)明: a)Bootstrap:一般用本地代碼實(shí)現(xiàn)畦韭,負(fù)責(zé)加載JVM基礎(chǔ)核心類庫(kù)(rt.jar); b)Extension:從 java.ext.dirs 系統(tǒng)屬性所指定的目錄中加載類庫(kù)肛跌,它的父加載器是 Bootstrap艺配; c)System:又叫應(yīng)用類加載器,其父類是Extension衍慎。它是應(yīng)用最廣泛的類加載器转唉。它從環(huán)境變量 classpath 或者系統(tǒng)屬性 java.class.path 所指定的目錄中記載類,是用戶自定義加載器的默認(rèn)父加載器稳捆。
類加載的五個(gè)過(guò)程:加載赠法、驗(yàn)證、準(zhǔn)備乔夯、解析砖织、初始化。
加載: 根據(jù)全限定名來(lái)獲取定義類的二進(jìn)制字節(jié)流,然后將該字節(jié)流所代表的靜態(tài)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),最后在java堆上生成一個(gè)代表該類的Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問入口.驗(yàn)證:主要時(shí)為了確保class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全.包含四個(gè)階段的驗(yàn)證過(guò)程: 1)文件格式驗(yàn)證:保證輸入的字節(jié)流能夠正確地解析并存儲(chǔ)在方法區(qū)之內(nèi),格式上符合描述一個(gè)java類型信息的要求 2)元數(shù)據(jù)驗(yàn)證:字節(jié)碼語(yǔ)義信息的驗(yàn)證,以保證描述的信息符合java語(yǔ)言規(guī)范.驗(yàn)證點(diǎn)有:這個(gè)類是否有父類等. 3)字節(jié)碼驗(yàn)證:主要是進(jìn)行數(shù)據(jù)流和控制流分析,保證被校驗(yàn)類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的行為. 4)符號(hào)引用驗(yàn)證:對(duì)符號(hào)引用轉(zhuǎn)化為直接引用過(guò)程的驗(yàn)證.準(zhǔn)備:為類變量分配內(nèi)存并設(shè)置變量的初始值,這些內(nèi)存在方法區(qū)進(jìn)行分配.解析:將虛擬機(jī)常量池中的符號(hào)引用轉(zhuǎn)化為直接引用的過(guò)程.解析主要是針對(duì)類或接口,字段,類方法,類幾口方法四類.初始化:執(zhí)行靜態(tài)變量的賦值操作以及靜態(tài)代碼塊,完成初識(shí)化.初始化過(guò)程保證了父類中定義的初始化優(yōu)先于子類的初始化.但接口不需要執(zhí)行父類的初始化.
雙親委派模型:
除了頂層的啟動(dòng)類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器.順序依次是: Bootstrap ClassLoader: 啟動(dòng)類加載器,加載java_home/lib中的類 Extension ClassLoader: 擴(kuò)展類加載器,加載java_home/lib/ext目錄下的類庫(kù) Application ClassLoader: 應(yīng)用程序類加載器,加載用戶類路徑上指定類庫(kù). 雙親委派模型的工作原理是:如果一個(gè)類加載器受到了類加載請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而把這個(gè)請(qǐng)求委派給父類加載器去完成,每一層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簾o(wú)法完成加載請(qǐng)求時(shí),加載器才嘗試自己加載.這種方式保證了Oject類在各個(gè)加載器加載環(huán)境中都是同一個(gè)類.
分派:靜態(tài)分派與動(dòng)態(tài)分派末荐。
多態(tài)性特征的一些最基本的體現(xiàn). 靜態(tài)類型是編譯器可知的,動(dòng)態(tài)類型是在運(yùn)行時(shí)可知.Human h =new Man(); Human是靜態(tài)類型,Man時(shí)動(dòng)態(tài)類型. 所有依賴于靜態(tài)類型定義方法執(zhí)行版本的分配動(dòng)作稱作靜態(tài)分派,最典型的應(yīng)用是方法重載.動(dòng)態(tài)分派是根據(jù)動(dòng)態(tài)類型來(lái)確定執(zhí)行的版本,所以只有到運(yùn)行時(shí)才能確定具體的執(zhí)行方法版本.典型的代表時(shí)重寫.其過(guò)程如下: 1)首先找到操作數(shù)棧棧頂?shù)牡谝粋€(gè)元素所執(zhí)向?qū)ο蟮膶?shí)際類型,記做C. 2) 如果在類型C中找到和常量中的描述符和簡(jiǎn)單名稱都相符的方法,則進(jìn)行范圍權(quán)限校驗(yàn).如果通過(guò)則返回該方法的直接引用,否則拋出IllegalAccessError異常. 3) 否則按照繼承關(guān)系從下往上一次對(duì)C的各個(gè)父類進(jìn)行第2步的搜索和驗(yàn)證過(guò)程. 4) 如果始終沒有找到舊拋出AbstractMethodError異常. 方法的接受者和方法的參數(shù)統(tǒng)稱方法宗量,根據(jù)分配基于多少中宗量可以分為單分派和多分派.java是靜態(tài)多分派,動(dòng)態(tài)分派屬于單分派.
動(dòng)態(tài)分派的實(shí)現(xiàn):動(dòng)態(tài)分派時(shí)非常頻繁的動(dòng)作,而且動(dòng)態(tài)分派的方法版本選擇過(guò)程需要運(yùn)行時(shí)在類的方法元數(shù)據(jù)中搜索合適的目標(biāo)方法,因此出于性能的考慮,在方法區(qū)中建立一個(gè)虛方法表,用來(lái)保存各個(gè)方法的實(shí)際入口地址.如果某個(gè)方法的子類中沒有被重新,那么子類的虛方法表里面的地址入口和父類相同方法的地址入口是一致的.都是指向父類的實(shí)現(xiàn)入口,如果子類中重寫了這個(gè)方法,子類方法表中的地址將會(huì)被替換為指向子類實(shí)現(xiàn)版本的入口地址.虛方法表在類加載的連接階段進(jìn)行初始化.
Jvm 自動(dòng)內(nèi)存管理(什么時(shí)候觸發(fā) gc )
http://jeromecen1021.blog.163.com/blog/static/18851527120117274624888/FULL GC 和 Minor GC 的觸發(fā)時(shí)間 程序員不能具體控制時(shí)間侧纯,系統(tǒng)在不可預(yù)測(cè)的時(shí)間調(diào)用System.gc()函數(shù)的時(shí)候;當(dāng)然可以通過(guò)調(diào)優(yōu)甲脏,用NewRatio控制newObject和oldObject的比例眶熬,用MaxTenuringThreshold 控制進(jìn)入oldObject的次數(shù),使得oldObject 存儲(chǔ)空間延遲達(dá)到full gc,從而使得計(jì)時(shí)器引發(fā)gc時(shí)間延遲OOM的時(shí)間延遲块请,以延長(zhǎng)對(duì)象生存期娜氏。
GC停頓原因,如何降低停頓
JVM如何調(diào)優(yōu)墩新、參數(shù)怎么調(diào)
jvm的體系結(jié)構(gòu)及各個(gè)部分的職責(zé)
JVM都有兩種機(jī)制牍白,一個(gè)是裝載具有合適名稱的類(類或是接口),包含類的裝載 連接 初始化的過(guò)程叫做類裝載子系統(tǒng)抖棘;另外的一個(gè)負(fù)責(zé)執(zhí)行包含在已裝載的類或接口中的指令,叫做運(yùn)行引擎狸涌。每個(gè)JVM又包括方法區(qū)切省、堆、Java棧帕胆、程序計(jì)數(shù)器和本地方法棧這五個(gè)部分朝捆,這幾個(gè)部分和類裝載機(jī)制與運(yùn)行引擎機(jī)制一起組成的體系結(jié)構(gòu)圖為:
JVM的每個(gè)實(shí)例都有一個(gè)它自己的方法域和一個(gè)堆,運(yùn)行于JVM內(nèi)的所有的線程都共享這些區(qū)域懒豹;當(dāng)虛擬機(jī)裝載類文件的時(shí)候芙盘,它解析其中的二進(jìn)制數(shù)據(jù)所包含的類信息驯用,并把它們放到方法域中;當(dāng)程序運(yùn)行的時(shí)候儒老,JVM把程序初始化的所有對(duì)象置于堆上蝴乔;而每個(gè)線程創(chuàng)建的時(shí)候,都會(huì)擁有自己的程序計(jì)數(shù)器和Java棧驮樊,其中程序計(jì)數(shù)器中的值指向下一條即將被執(zhí)行的指令薇正,線程的Java棧則存儲(chǔ)為該線程調(diào)用Java方法的狀態(tài);本地方法調(diào)用的狀態(tài)被存儲(chǔ)在本地方法棧囚衔,該方法棧依賴于具體的實(shí)現(xiàn)挖腰。
http://blog.csdn.net/dongdong_java/article/details/24797307http://blog.csdn.net/longyulu/article/details/8350622
如果想不被 GC 怎么辦
可以先說(shuō)那些對(duì)象可以被GC,然后說(shuō)java對(duì)象會(huì)不會(huì)回收,決定于是否還被引用练湿,不被引用了就有可能被GC回收猴仑,一直被引用著就不會(huì)被回收.
jvm性能調(diào)優(yōu)都做了什么
介紹GC 和GC Root不正常引用。
自己從classload 加載方式肥哎,加載機(jī)制說(shuō)開去辽俗,從程序運(yùn)行時(shí)數(shù)據(jù)區(qū),講到內(nèi)存分配贤姆,講到String常量池榆苞,講到JVM垃圾回收機(jī)制,算法霞捡,hotspot坐漏。反正就是各種擴(kuò)展
數(shù)組多大放在 JVM 老年代(不只是設(shè)置 PretenureSizeThreshold ,問通常多大碧信,沒做過(guò)一問便知)
老年代中數(shù)組的訪問方式
GC 算法赊琳,永久代對(duì)象如何 GC , GC 有環(huán)怎么處理
jvm 如何分配直接內(nèi)存??
new 對(duì)象如何不分配在堆而是棧上?
常量池解析
運(yùn)行期優(yōu)化
最佳實(shí)踐
Student s= new Student(),在內(nèi)存中做了那些事情
加載Student.class 文件進(jìn)內(nèi)存
在棧內(nèi)存為s開辟空間
在堆內(nèi)存為學(xué)術(shù)對(duì)象開辟空間
學(xué)生對(duì)象的成員變量進(jìn)行顯示初始化
通過(guò)構(gòu)造方法對(duì)學(xué)生對(duì)象變量賦值
學(xué)生對(duì)象初始完畢砰碴,把對(duì)象地址賦值給s變量
常用參數(shù)配置
參數(shù) 含義 默認(rèn)值 備注 -Xms:表示初始堆大小,默認(rèn)為物理內(nèi)存的1/64(<1GB) 默認(rèn)(MinHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存小于40%時(shí)躏筏,JVM就會(huì)增大堆直到-Xmx的最大限制. -Xmx 最大堆大小 物理內(nèi)存的1/4(<1GB) 默認(rèn)(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時(shí),JVM會(huì)減少堆直到 -Xms的最小限制 -Xmn 年輕代大小(1.4or lator) 注意:此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的呈枉。 整個(gè)堆大小=年輕代大小 + 年老代大小 + 持久代大小. 增大年輕代后,將會(huì)減小年老代大小.此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8 -XX:NewSize 設(shè)置年輕代大小(for 1.3/1.4)
-XX:MaxNewSize 年輕代最大值(for 1.3/1.4)
-XX:PermSize 設(shè)置持久代(perm gen)初始值 物理內(nèi)存的1/64
-XX:MaxPermSize 設(shè)置持久代最大值 物理內(nèi)存的1/4
-Xss 每個(gè)線程的堆棧大小 JDK5.0以后每個(gè)線程堆棧大小為1M,以前每個(gè)線程堆棧大小為256K.更具應(yīng)用的線程所需內(nèi)存大小進(jìn)行 調(diào)整.在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程.但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無(wú)限生成,經(jīng)驗(yàn)值在3000~5000左右 一般小的應(yīng)用趁尼, 如果棧不是很深, 應(yīng)該是128k夠用的 大的應(yīng)用建議使用256k猖辫。這個(gè)選項(xiàng)對(duì)性能影響比較大酥泞,需要嚴(yán)格的測(cè)試。(校長(zhǎng)) 和threadstacksize選項(xiàng)解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話: -Xss is translated in a VM flag named ThreadStackSize 一般設(shè)置這個(gè)值就可以了啃憎。 -XX:NewRatio 年輕代(包括Eden和兩個(gè)Survivor區(qū))與年老代的比值(除去持久代)
-XX:NewRatio = 4表示年輕代與年老代所占比值為1:4, 年輕代占整個(gè)堆棧的1/5 Xms = Xmx并且設(shè)置了Xmn的情況下芝囤,該參數(shù)不需要進(jìn)行設(shè)置。 -XX:SurvivorRatio Eden區(qū)與Survivor區(qū)的大小比值 設(shè)置為8,則兩個(gè)Survivor區(qū)與一個(gè)Eden區(qū)的比值為2:8,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/10 -XX:MaxTenuringThreshold 垃圾最大年齡 如果設(shè)置為0的話,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū),直接進(jìn)入年老代. 對(duì)于年老代比較多的應(yīng)用,可以提高效率.如果將此值設(shè)置為一個(gè)較大值,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對(duì)象再年輕代的存活 時(shí)間,增加在年輕代即被回收的概率 該參數(shù)只有在串行GC時(shí)才有效.
-XX:PretenureSizeThreshold 對(duì)象超過(guò)多大是直接在舊生代分配 0 單位字節(jié) 新生代采用Parallel Scavenge GC時(shí)無(wú)效 另一種直接在舊生代分配的情況是大的數(shù)組對(duì)象,且數(shù)組中無(wú)外部引用對(duì)象.