????1.JVM的內(nèi)存結(jié)構(gòu)和管理機制氏捞;
JVM實例:一個獨立運行的java程序棉饶,是進程級別
JVM執(zhí)行引擎:用戶運行程序的線程垦页,是JVM實例的一部分
JVM實例的誕生
當(dāng)啟動一個java程序時.一個JVM實例就誕生了紊册,任何一個擁有public static void main(string[] args)的函數(shù)都可以作為實例的運行啟點
? ? ? 2.? JVM實例運行
main作為程序初始化線程的起點,任何其他線程由其啟動。
JVM有兩種線程:守護線程和非守護線程男翰。守護線程由JVM使用另患。main啟動后將是非守護線程。
? ? ? 3.? JVM實例消亡
? 當(dāng)程序中所有非守護線程都中止時,JVM退出蛾绎;若安全管理器允許昆箕,程序也可以使用
Runtime類或者System.exit()退出。
JVM的生命周期
JVM主要包括四個部分:
1.類加載器(ClassLoader):在JVM啟動時或者在類運行時將需要的class加載到JVM中租冠。(右圖表示了從java源文件到JVM的整個過程鹏倘,可配合理解。 關(guān)于類的加載機制肺稀,可以參考http://blog.csdn.net/tonytfjing/article/details/47212291)
2.執(zhí)行引擎:負(fù)責(zé)執(zhí)行class文件中包含的字節(jié)碼指令(執(zhí)行引擎的工作機制第股,這里也不細(xì)說了应民,這里主要介紹JVM結(jié)構(gòu))话原;
3.內(nèi)存區(qū)(也叫運行時數(shù)據(jù)區(qū)):是在JVM運行的時候操作所分配的內(nèi)存區(qū)。運行時內(nèi)存區(qū)主要可以劃分為5個區(qū)域
? ? ? 方法區(qū)(Method Area):用于存儲類結(jié)構(gòu)信息的地方诲锹,包括常量池繁仁、靜態(tài)變量、構(gòu)造函數(shù)等归园。雖然JVM規(guī)范把方法區(qū)描述為堆的一個邏輯部分黄虱, 但它卻有個別名non-heap(非堆),所以大家不要搞混淆了庸诱。方法區(qū)還包含一個運行時常量池捻浦。
? ? ? java堆(Heap):存儲java實例或者對象的地方。這塊是GC的主要區(qū)域(后面解釋)桥爽。從存儲的內(nèi)容我們可以很容易知道朱灿,方法區(qū)和堆是被所有java線程共享的。
? ? ? java棧(Stack):java椖扑模總是和線程關(guān)聯(lián)在一起盗扒,每當(dāng)創(chuàng)建一個線程時,JVM就會為這個線程創(chuàng)建一個對應(yīng)的java棧。在這個java棧中又會包含多個棧幀侣灶,每運行一個方法就創(chuàng)建一個棧幀甸祭,用于存儲局部變量表、操作棧褥影、方法返回值等池户。每一個方法從調(diào)用直至執(zhí)行完成的過程,就對應(yīng)一個棧幀在java棧中入棧到出棧的過程伪阶。所以java棧是現(xiàn)成私有的煞檩。
? ? ? ? 程序計數(shù)器(PC Register):用于保存當(dāng)前線程執(zhí)行的內(nèi)存地址。由于JVM程序是多線程執(zhí)行的(線程輪流切換)栅贴,所以為了保證線程切換回來后斟湃,還能恢復(fù)到原先狀態(tài),就需要一個獨立的計數(shù)器檐薯,記錄之前中斷的地方凝赛,可見程序計數(shù)器也是線程私有的。
? ? ? ? 本地方法棧(Native Method Stack):和java棧的作用差不多坛缕,只不過是為JVM使用到的native方法服務(wù)的墓猎。
4.本地方法接口:主要是調(diào)用C或C++實現(xiàn)的本地方法及返回結(jié)果。
JVM主要包括四個部分:
Method Area 方法區(qū)又稱永久代(Permanent Generation)
? ? ? 方法區(qū)是被所有線程共享赚楚,該區(qū)域保存所有字段和方法字節(jié)碼毙沾,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在此定義宠页。
? ? ? 方法區(qū)中存放了每個Class的結(jié)構(gòu)信息左胞,包括常量池、字段描述举户、方法描述等等烤宙。
? ? ? VM Space 描述中對這個區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存俭嘁,也可以選擇固定大小或者可擴展外躺枕,甚至可以選擇不實現(xiàn)垃圾收集。相對來說供填,垃圾收集行為在這個區(qū)域是相對比較少發(fā)生的拐云,但并不是某些描述那樣永久代不會發(fā)生GC,這里的GC主要是對常量池的回收和對類的卸載近她,雖然回收的“成績”一般也比較差強人意叉瘩,尤其是類卸載,條件相當(dāng)苛刻泄私。
? ? ? 運行時常量池(Runtime Constant Pool)也方法區(qū)存放房揭。Class文件中除了有類的版本备闲、字段惩系、方法钦奋、接口等描述等信息外妻率,還有一項信息是常量表(constant_pool table)恭陡,用于存放編譯期已可知的常量刽宪,這部分內(nèi)容將在類加載后進入方法區(qū)(永久代)存放埋涧。但是Java語言并不要求常量一定只有編譯期預(yù)置入Class的常量表的內(nèi)容才能進入方法區(qū)常量池围肥,運行期間也可將新內(nèi)容放入常量池(最典型的String.intern()方法)戏羽。 運行時常量池是方法區(qū)的一部分梧奢,自然受到方法區(qū)內(nèi)存的限制狱掂,當(dāng)常量池?zé)o法在申請到內(nèi)存時會拋出OutOfMemoryError異常
? ? ? 2. PC Register程序計數(shù)器
每個線程都有一個程序計數(shù)器,就是一個指針亲轨,指向方法區(qū)中的方法字節(jié)碼趋惨,由執(zhí)行引擎讀取下一條指令。每一個Java線程都有一個程序計數(shù)器來用于保存程序執(zhí)行到當(dāng)前方法的哪一個指令惦蚊,對于非Native方法器虾,這個區(qū)域記錄的是正在執(zhí)行的VM原語的地址,如果正在執(zhí)行的是Natvie方法蹦锋,這個區(qū)域則為空(undefined)兆沙。此內(nèi)存區(qū)域是唯一一個在VM Spec中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。
? ? ? 3.Native Method Stack 本地方法棧
本地方法棧是為虛擬機使用到的Native方法服務(wù)莉掂。它的實現(xiàn)的語言葛圃、方式與結(jié)構(gòu)并沒有強制規(guī)定,甚至有的虛擬機(譬如Sun Hotspot虛擬機)直接就把本地方法棧和JVM棧合二為一憎妙。這個區(qū)域也會拋出StackOverflowError和OutOfMemoryError異常库正。
? ? ? 4.Stack棧也叫棧內(nèi)存
VM棧的生命周期也是與線程相同。VM棧描述的是Java方法調(diào)用的內(nèi)存模型:每個方法被執(zhí)行的時候尚氛,都會同時創(chuàng)建一個幀(Frame)用于存儲本地變量表诀诊、操作棧洞渤、動態(tài)鏈接阅嘶、方法出入口等信息。每一個方法的調(diào)用至完成载迄,就意味著一個幀在VM棧中的入棧至出棧的過程讯柔。
JVM的內(nèi)存指令區(qū)。是在線程創(chuàng)建時創(chuàng)建护昧,它的生命期是跟隨線程的生命期魂迄,線程結(jié)束棧內(nèi)存也就釋放,對于棧來說不存在垃圾回收問題惋耙,只要線程一結(jié)束捣炬,該棧就Over熊昌。問題出來了:棧中存的是那些數(shù)據(jù)呢?又什么是格式呢湿酸?
棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在婿屹,棧幀是一個內(nèi)存區(qū)塊,是一個數(shù)據(jù)集推溃,是一個有關(guān)方法(Method)和運行期數(shù)據(jù)的數(shù)據(jù)集昂利,當(dāng)一個方法A被調(diào)用時就產(chǎn)生了一個棧幀F(xiàn)1,并被壓入到棧中铁坎,A方法又調(diào)用了B方法蜂奸,于是產(chǎn)生棧幀F(xiàn)2也被壓入棧,執(zhí)行完畢后硬萍,先彈出F2棧幀扩所,再彈出F1棧幀,遵循“先進后出”原則朴乖。
那棧幀中到底存在著什么數(shù)據(jù)呢碌奉?棧幀中主要保存3類數(shù)據(jù):本地變量(Local Variables),包括輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量寒砖;棧操作(Operand Stack)赐劣,記錄出棧、入棧的操作哩都;棧幀數(shù)據(jù)(Frame Data)魁兼,包括類文件、方法等漠嵌。
VM棧中各個幀的本地變量表部分,本地變量表存放了編譯期可知的各種標(biāo)量類型(boolean咐汞、byte、char儒鹿、short化撕、int、float约炎、long植阴、double)、對象引用(不是對象本身圾浅,僅僅是一個引用指針)掠手、方法返回地址等。其中l(wèi)ong和double會占用2個本地變量空間(32bit)狸捕,其余占用1個喷鸽。
本地變量表在進入方法時進行分配,當(dāng)進入一個方法時灸拍,這個方法需要在幀中分配多大的本地變量是一件完全確定的事情做祝,在方法運行期間不改變本地變量表的大小砾省。
在VM Spec中對這個區(qū)域規(guī)定了2中異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常混槐;如果VM棿慷辏可以動態(tài)擴展(VM Spec中允許固定長度的VM棧),當(dāng)擴展時無法申請到足夠內(nèi)存則拋出OutOfMemoryError異常纵隔。
? ? ? 5. Heap 堆內(nèi)存
一個JVM實例只存在一個堆類存翻诉,堆內(nèi)存的大小是可以調(diào)節(jié)的。
Java堆可以處于物理上不連續(xù)的內(nèi)存空間捌刮,它邏輯上是連續(xù)的即可碰煌,就像我們的磁盤空間一樣。實現(xiàn)時可以選擇實現(xiàn)成固定大小的绅作,也可以是可擴展的芦圾,
不過當(dāng)前所有商業(yè)的虛擬機都是按照可擴展來實現(xiàn)的(通過-Xmx和-Xms控制)。如果在堆中無法分配內(nèi)存俄认,并且堆也無法再擴展時个少,將會拋出OutOfMemoryError異常。
類加載器讀取了類文件后眯杏,需要把類夜焦、方法、常變量放到堆內(nèi)存中岂贩,以方便執(zhí)行器執(zhí)行茫经,堆內(nèi)存分為三部分:
1)? ? ? ? Permanent Space永久存儲區(qū)(也叫方法區(qū))
永久存儲區(qū)是一個常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù)萎津,也就是說它存儲的是運行環(huán)境必須的類信息卸伞,被裝載進此區(qū)域的數(shù)據(jù)是不會被垃圾回收器回收掉的,關(guān)閉JVM才會釋放此區(qū)域所占用的內(nèi)存锉屈。
2)? ? ? ? Young Generation Space 新生區(qū)
新生區(qū)是類的誕生荤傲、成長、消亡的區(qū)域颈渊,一個類在這里產(chǎn)生遂黍,應(yīng)用,最后被垃圾回收器收集儡炼,結(jié)束生命妓湘。新生區(qū)又分為兩部分:伊甸區(qū)(Eden space)和幸存者區(qū)(Survivor pace)查蓉,所有的類都是在伊甸區(qū)被new出來的乌询。幸存區(qū)有兩個: 0區(qū)(Survivor 0 space)和1區(qū)(Survivor 1 space)。當(dāng)伊甸園的空間用完時豌研,程序又需要創(chuàng)建對象妹田,JVM的垃圾回收器將對伊甸園區(qū)進行垃圾回收唬党,將伊甸園區(qū)中的不再被其他對象所引用的對象進行銷毀。然后將伊甸園中的剩余對象移動到幸存0區(qū)鬼佣。若幸存0區(qū)也滿了驶拱,再對該區(qū)進行垃圾回收,然后移動到1區(qū)晶衷。那如果1區(qū)也滿了呢蓝纲?再移動到養(yǎng)老區(qū)。
3)? ? ? ? Tenure generation space養(yǎng)老區(qū)
養(yǎng)老區(qū)用于保存從新生區(qū)篩選出來的JAVA對象晌纫,一般池對象都在這個區(qū)域活躍税迷。
? 6. 本機直接內(nèi)存(Direct Memory)
直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,它根本就是本機內(nèi)存而不是VM直接管理的區(qū)域锹漱。但是這部分內(nèi)存也會導(dǎo)致OutOfMemoryError異常出現(xiàn)箭养,因此我們放到這里一起描述。在JDK1.4中新加入了NIO類哥牍,引入一種基于渠道與緩沖區(qū)的I/O方式毕泌,它可以通過本機Native函數(shù)庫直接分配本機內(nèi)存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作嗅辣。這樣能在一些場景中顯著提高性能撼泛,因為避免了在Java對和本機堆中來回復(fù)制數(shù)據(jù)。
顯然本機直接內(nèi)存的分配不會受到Java堆大小的限制澡谭,但是即然是內(nèi)存那肯定還是要
受到本機物理內(nèi)存(包括SWAP區(qū)或者Windows虛擬內(nèi)存)的限制的坎弯,一般服務(wù)器管理員配置JVM參數(shù)時,
會根據(jù)實際內(nèi)存設(shè)置-Xmx等參數(shù)信息译暂,但經(jīng)常忽略掉直接內(nèi)存抠忘,使得各個內(nèi)存區(qū)域總和大于物理內(nèi)存限制(包括物理的和操作系統(tǒng)級的限制),而導(dǎo)致動態(tài)擴展時出現(xiàn)OutOfMemoryError異常外永。
JVM的內(nèi)存管理
jvm的垃圾回收機制
JVM中自動的對象內(nèi)存回收機制稱為GC(Garbage Collection)崎脉。
為什么要進行垃圾回收?隨著程序的運行伯顶,內(nèi)存中存在的實例對象囚灼、變量等信息占據(jù)的內(nèi)存越來越多,如果不及時進行垃圾回收祭衩,必然會帶來程序性能的下降灶体,甚至?xí)驗榭捎脙?nèi)存不足造成一些不必要的系統(tǒng)異常。
哪些“垃圾”需要回收掐暮?
在我們上面介紹的六大區(qū)中蝎抽,有三個是不需要進行垃圾回收的:程序計數(shù)器、JVM棧路克、本地方法棧樟结。因為它們的生命周期是和線程同步的养交,隨著線程的銷毀,它們占用的內(nèi)存會自動釋放瓢宦,所以只有方法區(qū)和堆需要進行GC碎连。方法區(qū)回收也很少,一般是堆需要GC驮履,具體到哪些對象的話鱼辙,簡單概況一句話:如果某個對象已經(jīng)不存在任何引用,那么它可以被回收玫镐。通俗解釋一下就是說座每,如果一個對象,已經(jīng)沒有什么作用了摘悴,就可以被當(dāng)廢棄物被回收了峭梳。
什么時候進行垃圾回收?
根據(jù)一個經(jīng)典的引用計數(shù)算法蹂喻,每個對象添加一個引用計數(shù)器葱椭,每被引用一次,計數(shù)器加1口四,失去引用孵运,計數(shù)器減1,當(dāng)計數(shù)器在一段時間內(nèi)保持為0時蔓彩,該對象就認(rèn)為是可以被回收得了。但是赤嚼,這個算法有明顯的缺陷:當(dāng)兩個對象相互引用,但是二者已經(jīng)沒有作用時更卒,按照常規(guī)等孵,應(yīng)該對其進行垃圾回收,但是其相互引用蹂空,又不符合垃圾回收的條件俯萌,因此無法完美處理這塊內(nèi)存清理,因此Sun的JVM并沒有采用引用計數(shù)算法來進行垃圾回收上枕。而是采用一個叫:根搜索算法
基本思想就是:從一個叫GC Roots的對象開始咐熙,向下搜索,如果一個對象不能到達(dá)GC Roots對象的時候辨萍,說明它已經(jīng)不再被引用棋恼,即可被進行垃圾回收(此處 暫且這樣理解,其實事實還有一些不同,當(dāng)一個對象不再被引用時蘸泻,并沒有完全“死亡”琉苇,如果類重寫了finalize()方法嘲玫,且沒有被系統(tǒng)調(diào)用過悦施,那么系統(tǒng)會調(diào)用一次finalize()方法,以完成最后的工作去团,在這期間抡诞,如果可以將對象重新與任何一個和GC Roots有引用的對象相關(guān)聯(lián),則該對象可以“重生”土陪,如果不可以昼汗,那么就說明徹底可以被回收了),如上圖中的Object5鬼雀、Object6顷窒、Object7,雖然它們3個依然可能相互引用源哩,但是總體來說鞋吉,它們已經(jīng)沒有作用了,這樣就解決了引用計數(shù)算法無法解決的問題励烦。
GC基本原理
為將內(nèi)存中不再被使用的對象進行回收谓着,消耗資源和時間。
1)對新生代對象的收集稱為minor GC
2) 對舊生代對象的收集成為Full? GC
3) 程序中主動調(diào)用System.GC()強制執(zhí)行的GC稱為FULL GC
對象引用類型分為強引用赊锚、軟引用屉栓、弱引用和虛引用。
1)? ? ? ? 強引用:就是我們一般聲明對象是時虛擬機生成的引用阿纤,強引用環(huán)境下欠拾,垃圾回收時需要嚴(yán)格判斷當(dāng)前對象是否被強引用藐窄,如果被強引用酬土,則不會被垃圾回收。
2)? ? ? ? 軟引用:軟引用一般被做為緩存來使用叽唱。與強引用的區(qū)別是微宝,軟引用在垃圾回收時,虛擬機會根據(jù)當(dāng)前系統(tǒng)的剩余內(nèi)存來決定是否對軟引用進行回收镶摘。如果剩余內(nèi)存比較緊張凄敢,則虛擬機會回收軟引用所引用的空間;如果剩余內(nèi)存相對富裕涝缝,則不會進行回收譬重。換句話說,虛擬機在發(fā)生OutOfMemory時消恍,肯定是沒有軟引用存在的狠怨。
3)? ? ? ? 弱引用:弱引用與軟引用類似佣赖,都是作為緩存來使用记盒。但與軟引用不同,弱引用在進行垃圾回收時俩檬,是一定會被回收掉的棚辽,因此其生命周期只存在于一個垃圾回收周期內(nèi)屈藐。
4)? ? ? ? "虛引用":就是形同虛設(shè),與其他幾種引用都不同联逻,虛引用并不會決定對象的生命周期包归。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣爱态,在任何時候都可能被垃圾回收境钟。
虛引用主要用來跟蹤對象被垃圾回收的活動慨削。虛引用與軟引用和弱引用的一個區(qū)別在于:虛引用必須和引用隊列(ReferenceQueue)聯(lián)合使用缚态。當(dāng)垃 圾回收器準(zhǔn)備回收一個對象時堤瘤,如果發(fā)現(xiàn)它還有虛引用玫芦,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之關(guān)聯(lián)的引用隊列中桥帆。程序可以通過判斷引用隊列中是 否已經(jīng)加入了虛引用老虫,來了解被引用的對象是否將要被垃圾回收茫多。程序如果發(fā)現(xiàn)某個虛引用已經(jīng)被加入到引用隊列,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動夺欲。
強引用不用說今膊,我們系統(tǒng)一般在使用時都是用的強引用万细。而“軟引用”和“弱引用”比較少見纸泄。他們一般被作為緩存使用聘裁,而且一般是在內(nèi)存大小比較受限的情況下做為緩存衡便。因為如果內(nèi)存足夠大的話洋访,可以直接使用強引用作為緩存即可姻政,同時可控性更高汁展。因而,他們常見的是被使用在桌面應(yīng)用系統(tǒng)的緩存侈咕。
特別注意耀销,在世紀(jì)程序設(shè)計中一般很少使用弱引用與虛引用熊尉,使用軟用的情況較多桥状,這是因為軟引用可以加速JVM對垃圾內(nèi)存的回收速度辅斟,可以維護系統(tǒng)的運行安全士飒,防止內(nèi)存溢出(OutOfMemory)等問題的產(chǎn)生。
如何進行垃圾回收扰藕?
本塊內(nèi)容以介紹垃圾回收算法為主邓深,因為我們前面有介紹,內(nèi)存主要被分為三塊冬耿,新生代萌壳、舊生代袱瓮、持久代尺借。三代的特點不同褐望,造就了他們所用的GC算法不同串前,新生代適合那些生命周期較短荡碾,頻繁創(chuàng)建及銷毀的對象,舊生代適合生命周期相對較長的對象劳殖,持久代在Sun HotSpot中就是指方法區(qū)(有些JVM中根本就沒有持久代這中說法)哆姻。首先介紹下新生代矛缨、舊生代帖旨、持久代的概念及特點:
新生代:New Generation或者Young Generation解阅。上面大致分為Eden區(qū)和Survivor區(qū),Survivor區(qū)又分為大小相同的兩部分:FromSpace 和ToSpace述召。新建的對象都是用新生代分配內(nèi)存积暖,Eden空間不足的時候,會把存活的對象轉(zhuǎn)移到Survivor中凉蜂,新生代的大小可以由-Xmn來控制窿吩,也可以用-XX:SurvivorRatio來控制Eden和Survivor的比例.
舊生代:Old Generation纫雁。用于存放新生代中經(jīng)過多次垃圾回收仍然存活的對象轧邪,例如緩存對象忌愚。舊生代占用大小為-Xmx值減去-Xmn對應(yīng)的值硕糊。
持久代:Permanent Generation简十。在Sun的JVM中就是方法區(qū)的意思螟蝙,盡管有些JVM大多沒有這一代胰默。主要存放常量及類的一些信息默認(rèn)最小值為16MB初坠,最大值為64MB彭雾,可通過-XX:PermSize及-XX:MaxPermSize來設(shè)置最小值和最大值薯酝。
? 5.常見的GC算法:
標(biāo)記-清除算法(Mark-Sweep)
最基礎(chǔ)的GC算法,將需要進行回收的對象做標(biāo)記者填,之后掃描,有標(biāo)記的進行回收占哟,這樣就產(chǎn)生兩個步驟:標(biāo)記和清除榨乎。這個算法效率不高蜜暑,而且在清理完成后會產(chǎn)生內(nèi)存碎片肛捍,這樣拙毫,如果有大對象需要連續(xù)的內(nèi)存空間時恬偷,還需要進行碎片整理,所以袍患,此算法需要改進。
? ? ? ? 復(fù)制算法(Copying)
前面我們談過竣付,新生代內(nèi)存分為了三份诡延,Eden區(qū)和2塊Survivor區(qū),一般Sun的JVM會將Eden區(qū)和Survivor區(qū)的比例調(diào)為8:1古胆,保證有一塊Survivor區(qū)是空閑的肆良,這樣,在垃圾回收的時候逸绎,將不需要進行回收的對象放在空閑的Survivor區(qū)惹恃,然后將Eden區(qū)和第一塊Survivor區(qū)進行完全清理棺牧,這樣有一個問題醉锄,就是如果第二塊Survivor區(qū)的空間不夠大怎么辦?這個時候烟勋,就需要當(dāng)Survivor區(qū)不夠用的時候,暫時借持久代的內(nèi)存用一下。此算法適用于新生代蛹找。
? ? ? ? 標(biāo)記-整理(或叫壓縮)算法(Mark-Compact)
和標(biāo)記-清楚算法前半段一樣届慈,只是在標(biāo)記了不需要進行回收的對象后,將標(biāo)記過的對象移動到一起,使得內(nèi)存連續(xù)嫂拴,這樣箱沦,只要將標(biāo)記邊界以外的內(nèi)存清理就行了。此算法適用于持久代峡继。
? ? ? 6.常見的垃圾收集器:
根據(jù)上面說的諸多算法儡循,每個JVM都有不同的實現(xiàn),我們首先介紹三種實際的垃圾回收器:串行GC(SerialGC)腹侣、并行回收GC(Parallel Scavenge)和并行GC(ParNew)。
1)、Serial GC。是最基本、最古老的收集器,但是現(xiàn)在依然被廣泛使用,是一種單線程垃圾回收機制,而且不僅如此楷拳,它最大的特點就是在進行垃圾回收的時候她混,需要將所有正在執(zhí)行的線程暫停(Stop The World),對于有些應(yīng)用這是難以接受的,但是我們可以這樣想砚作,只要我們能夠做到將它所停頓的時間控制在N個毫秒范圍內(nèi)压昼,大多數(shù)應(yīng)用我們還是可以接受的窍霞,而且事實是它并沒有讓我們失望冷溃,幾十毫米的停頓我們作為客戶機(Client)是完全可以接受的年柠,該收集器適用于單CPU味赃、新生代空間較小及對暫停時間要求不是非常高的應(yīng)用上,是client級別默認(rèn)的GC方式,可以通過-XX:+UseSerialGC來強制指定。
2)、ParNew GC」置铮基本和Serial GC一樣弓坞,但本質(zhì)區(qū)別是加入了多線程機制,提高了效率,這樣它就可以被用在服務(wù)器端(Server)上,同時它可以與CMS GC配合,所以欠窒,更加有理由將它置于Server端型将。
3)、Parallel Scavenge GC荐虐。在整個掃描和復(fù)制過程采用多線程的方式來進行腕铸,適用于多CPU恬惯、對暫停時間要求較短的應(yīng)用上酪耳,是server級別默認(rèn)采用的GC方式碗暗,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定線程數(shù)碰缔。以下給出幾組使用組合:
4)、CMS (Concurrent Mark Sweep)收集器拣凹。該收集器目標(biāo)就是解決Serial GC 的停頓問題恨豁,以達(dá)到最短回收時間嚣镜。常見的B/S架構(gòu)的應(yīng)用就適合用這種收集器,因為其高并發(fā)橘蜜、高響應(yīng)的特點菊匿。CMS收集器是基于“標(biāo)記-清除”算法實現(xiàn)的,整個收集過程大致分為4個步驟:初始標(biāo)記(CMS initial mark)计福、并發(fā)標(biāo)記(CMS concurrenr mark)跌捆、重新標(biāo)記(CMS remark)、并發(fā)清除(CMS concurrent sweep)象颖。
CMS收集器的優(yōu)點:并發(fā)收集佩厚、低停頓,但是CMS還遠(yuǎn)遠(yuǎn)達(dá)不到完美说订。
5)抄瓦、G1收集器潮瓶。相比CMS收集器有不少改進,首先基于標(biāo)記-整理算法闺鲸,不會產(chǎn)生內(nèi)存碎片問題筋讨,其次,可以比較精確的控制停頓摸恍,此處不再詳細(xì)介紹。
6)赤屋、Serial Old立镶。Serial Old是Serial收集器的老年代版本,它同樣使用一個單線程執(zhí)行收集类早,使用“標(biāo)記-整理”算法媚媒。。
7)涩僻、Parallel Old缭召。Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法逆日。
8)嵌巷、RTSJ垃圾收集器,用于Java實時編程室抽,后續(xù)會補充介紹搪哪。
摘要:https://www.cnblogs.com/dragonsuc/p/4380409.html
JVM垃圾回收機制
一個java類的完整的生命周期會經(jīng)歷加載、連接坪圾、初始化晓折、使用、和卸載五個階段
1.加載
我們編寫一個java的源文件兽泄,經(jīng)過編譯后生成一個后綴名為.class的文件漓概,這結(jié)合四字節(jié)碼文件,
java虛擬機就識別這種文件病梢,java的生命周期就是class文件從加載到消亡的過程胃珍。
關(guān)于加載,其實飘千,就是將源文件的class文件找到類的信息將其加載到方法區(qū)中堂鲜,
然后在堆區(qū)中實例化一個java.lang.Class對象,作為方法區(qū)中這個類的信息的入口护奈。
但是這一功能是在JVM之外實現(xiàn)的缔莲,主要的原因是方便讓應(yīng)用程序自己決定如何獲取這個類,
在不同的虛擬機實現(xiàn)的方式不一定相同霉旗,hotspot虛擬機是采用需要時在加載的方式痴奏,
也有其他是先預(yù)先加載的蛀骇。
2.連接
一般會跟加載階段和初始化階段交叉進行,過程由三部分組成:驗證读拆、準(zhǔn)備和解析三步
(1)驗證:確定該類是否符合java語言的規(guī)范擅憔,有沒有屬性和行為的重復(fù),繼承是否合理檐晕,總之暑诸,就是保證jvm能夠執(zhí)行
(2)準(zhǔn)備:主要做的就是為由static修飾的成員變量分配內(nèi)存,并設(shè)置默認(rèn)的初始值
默認(rèn)初始值如下:
1.八種基本數(shù)據(jù)類型默認(rèn)的初始值是0
2.引用類型默認(rèn)的初始值是null
3.有static final修飾的會直接賦值辟灰,例如:static final int x=10个榕;則默認(rèn)就是10.
(3)解析:這一階段的任務(wù)就是把常量池中的符號引用轉(zhuǎn)換為直接引用,說白了就是jvm會將所有的類或接口名芥喇、字段名西采、方法名轉(zhuǎn)換為具體的內(nèi)存地址。
3.初始化
這個階段就是將靜態(tài)變量(類變量)賦值的過程继控,即只有static修飾的才能被初始化械馆,執(zhí)行的順序就是:
父類靜態(tài)域或著靜態(tài)代碼塊,然后是子類靜態(tài)域或者子類靜態(tài)代碼塊
4.使用
在類的使用過程中依然存在三步:對象實例化武通、垃圾收集霹崎、對象終結(jié)
(1)對象實例化:就是執(zhí)行類中構(gòu)造函數(shù)的內(nèi)容,如果該類存在父類JVM會通過顯示或者隱示的方式先執(zhí)行父類的構(gòu)造函數(shù)厅须,在堆內(nèi)存中為父類的實例變量開辟空間仿畸,并賦予默認(rèn)的初始值,然后在根據(jù)構(gòu)造函數(shù)的代碼內(nèi)容將真正的值賦予實例變量本身朗和,然后错沽,引用變量獲取對象的首地址,通過操作對象來調(diào)用實例變量和方法
(2)垃圾收集:當(dāng)對象不再被引用的時候眶拉,就會被虛擬機標(biāo)上特別的垃圾記號千埃,在堆中等待GC回收
(3)對象的終結(jié):對象被GC回收后,對象就不再存在忆植,對象的生命也就走到了盡頭
5.類卸載
即類的生命周期走到了最后一步放可,程序中不再有該類的引用,該類也就會被JVM執(zhí)行垃圾回收朝刊,從此生命結(jié)束…
摘要:https://www.cnblogs.com/ipetergo/p/6441310.html
Java類加載機制
HashMap:按照特性來說明一下耀里,儲存的是鍵值對,線程不安全拾氓,非Synchronied冯挎,儲存的比較快,能夠接受null咙鞍。按照工作原理來敘述一下房官,Map的put(key趾徽,value)來儲存元素,通過get(key)來得到value值翰守,通過hash算法來計算hascode值孵奶,根據(jù)hashcode值來決定bucket(桶),儲存結(jié)構(gòu)就算哈希表蜡峰。
提問:兩個hashcode相同的時候會發(fā)生說明了袁?
hashcode相同,bucket的位置會相同事示,也就是說會發(fā)生碰撞早像,哈希表中的結(jié)構(gòu)其實有鏈表(LinkedList),這種沖突通過將元素儲存到LinkedList中肖爵,解決碰撞。儲存順序是仿在表頭臀脏。
如果兩個鍵的hashcode相同劝堪,如何獲取值對象?
如果兩個鍵的hashcode相同揉稚,我們通過key.equals()找到LinkedList中正確的節(jié)點秒啦。
如果HashMap的大小超過了負(fù)載因子?怎么辦搀玖?
默認(rèn)的HashMap里面的負(fù)載因子大小是0.75余境,將會重新創(chuàng)建一個原來HashMap大小的
兩倍bucket數(shù)組。
重新調(diào)整的話會出現(xiàn)什么問題灌诅?
多線程情況下會出現(xiàn)競爭問題芳来,因為你在調(diào)節(jié)的時候,LinkedList儲存是按照順序儲存猜拾,調(diào)節(jié)的時候回將原來最先儲存的元素(也就是最下面的)遍歷即舌,多線程就好試圖重新調(diào)整,這個時候就會出現(xiàn)死循環(huán)挎袜。
為什么要用String顽聂、Wrapper類,來作為鍵值對盯仪?因為他們一般不是不可變的紊搪,源碼上面final,使用不可變類全景,而且重寫了equals和hashcode方法耀石,避免了鍵值對改寫。提高HashMap性能蚪燕。
使用CocurrentHashMap代替Hashtable娶牌?
可以奔浅,但是Hashtable提供的線程更加安全。
hashMap 面試題
工廠模式分為:簡單工廠模式诗良,工廠方法模式和抽象工廠模式
工廠模式汹桦,顧名思義,最少有一個生產(chǎn)產(chǎn)品的機器存在的工廠Factory鉴裹,與此同時舞骆,也要有一個構(gòu)建好的產(chǎn)品模塊Product。所以径荔,我們要用到Factory來創(chuàng)造Product督禽。在簡單工廠模式中,有幾種角色存在总处。一個是所有產(chǎn)品的父類P狈惫,即所有產(chǎn)品的模板;另外一個是繼承了父類P的產(chǎn)品子類p1鹦马,p2...;當(dāng)然胧谈,最重要的是Factory,在Factory中可以將這些的產(chǎn)品實例化荸频,根據(jù)需求來將Factory和Product產(chǎn)生聯(lián)系菱肖。
簡單工廠優(yōu)點:
這樣模塊清晰化,每個部分都各司其職旭从,分工明確稳强,代碼就實現(xiàn)最漸層意義上的“可維護”啦。說到缺點和悦,當(dāng)我們需要增加一產(chǎn)品退疫,比如在計算機中加入一個新的功能,可以求M的N次方摹闽,這樣個小功能我們就要去添加一個新的類蹄咖,同時我們需要在Factory中改動到switch里面的代碼,這是耦合性很高的表現(xiàn)啦付鹿,所以我們就有了“工廠模式”的出現(xiàn)啦澜汤。
優(yōu)缺點
a.在客戶端Client中可以將工廠模式的主要結(jié)構(gòu)看著很清楚,首先我們要有IFactory這個工廠的父接口舵匾,所有的子類或者子接口都可以實現(xiàn)它俊抵。AddFactory則是子類的代表之一,所以利用java的多態(tài)來實現(xiàn)坐梯,降低代碼的耦合性徽诲。而同時每個子工廠中擁有每條生產(chǎn)獨特產(chǎn)品的生產(chǎn)線。由此,工廠和產(chǎn)品掛上鉤了谎替,聯(lián)系上了偷溺。每個子工廠生產(chǎn)出來的都是獨特的產(chǎn)品。
b.比“簡單工廠模式”的優(yōu)缺點
優(yōu)點:克服了簡單工廠違背開放-封閉原則的缺點钱贯,又保留了封裝對象創(chuàng)建過程的優(yōu)點,降低客戶端和工廠的耦合性挫掏,所以說“工廠模式”是“簡單工廠模式”的進一步抽象和推廣。
缺點:每增加一個產(chǎn)品秩命,相應(yīng)的也要增加一個子工廠尉共,加大了額外的開發(fā)量。
基本上就是能記得就只有這些了弃锐,大家將就著看看袄友,祝去面試或者將要面試的你們順利!