【正文】
。 因此,從來都不執(zhí)行的代碼可能永遠(yuǎn)都不被 JIT 編譯。 這個(gè)時(shí)候,一個(gè)可以利用的方式就是直接讓 JIT 編譯所有的代碼,但是這樣做有兩個(gè)缺點(diǎn): 這種方式的應(yīng)用由于混雜在程序的整個(gè)生命周期里,所以能累加起來,從而導(dǎo)致程序執(zhí)行起來會花費(fèi)一些額外的時(shí)間;另外它也增加了可執(zhí)行程序的尺寸(字節(jié)碼要比已擴(kuò)展的 JIT 代碼壓縮度高出很多),因此有可能導(dǎo)致虛擬內(nèi)存的使用,結(jié)果明顯地降低了程序的運(yùn)行效率。 JIT 編譯器能部分或者全部把程序轉(zhuǎn)換成機(jī)器能夠識別的代碼,所以程序就不需要 JVM 的解釋了,結(jié)果是程序運(yùn) 行起來快很多。 ”JVM 里,可能會有一些附帶的加速技術(shù)。 9 同樣地, JVM 也會跟 蹤 markandsweep 的運(yùn)行效果,如果堆開始變得零碎不堪,垃收集又會轉(zhuǎn)回 stopandcopy 模式。大的對象仍舊不拷貝(他們只是把自己的生成記數(shù)增加),而那些包含小對象的塊會被拷貝和壓縮。 這種方式解決了通常情況下許多短期生存的暫時(shí)對象。每一個(gè)塊都有一個(gè)生成數(shù)用來跟蹤它是否還活著。 嚴(yán)格意義上的 stopandcopy 在可以釋放舊堆之前,需要從源堆里拷貝每一個(gè)活著的對象到新的堆里,這會耗費(fèi)大量內(nèi)存。 正如前面提到的,在這里描述的 JVM 里,內(nèi)存被分配成大的塊。相反,當(dāng)內(nèi)存閑置空間少的時(shí)候, Sun 的垃圾收集器會終止程序運(yùn)行。 stopandcopy 的思想是垃圾收集不在后臺完成,相反,程序需要停止運(yùn)行的同時(shí)垃圾收集開始工作。在清掃過程中,死對象被釋放存貯空間。 不過,每次它找到活對象的時(shí)候,那個(gè)對象被做以標(biāo)記,而且對象還不會被收集起來。 處理一般的垃圾收集工作, markandsweep 表現(xiàn)得相當(dāng)?shù)芈?,但是?dāng)你的程序生成很少或者不生成垃圾時(shí),它又運(yùn)行得很快。 為了阻止這種情況的發(fā)生,一些 JVMs 會探測沒有新垃圾產(chǎn)生的時(shí)機(jī),并且會轉(zhuǎn)向?qū)嵤┝硗庖粋€(gè)完全不同的垃圾收集方案。 一旦 8 你地程序趨向于穩(wěn)定的時(shí)候,它可能生成很少或者幾乎不生成垃圾。 一些 JVMs 通過依據(jù)實(shí)際所需來為堆分配大塊內(nèi)存,然后很簡單地從一個(gè)塊拷貝對象到另一個(gè)。 有兩個(gè)問題使這種所謂的 “拷貝型收集器 ”缺乏效率。 這些引用一旦發(fā)現(xiàn)就會被修改。當(dāng)然,對象從一個(gè)地方移動到另一個(gè)地方的時(shí)候,所有指向?qū)ο蟮囊帽仨毾鄳?yīng)改變。然后,每一個(gè)活著的對象從一個(gè)堆里被拷貝到另一個(gè)堆里,同時(shí)被拷貝的活對象和死的對象被當(dāng)作垃圾遺棄。其中的一個(gè)變體就是 stopandcopy。注意,這里不存在分離的自我引用的對象組 ——他們只是沒被查找到,因此被自動當(dāng)作垃圾。對于你找到的每個(gè)單引用, 你必須找到它所指向的對象,然后發(fā)覺那個(gè)對象的所有引用,接著找到那些引用所指向的所有對象,依次類推,直到你歷遍整個(gè)由棧和靜態(tài)存貯器里的引用所形成的網(wǎng)。這種鏈?zhǔn)降牟檎曳绞娇赡軞v遍幾個(gè)層次的對象組。有一種執(zhí)行更快的垃圾收集方案,這種方案中垃圾收集不是建立在引用記數(shù)的基礎(chǔ)上。 定位這種自我引用的對象組需要垃圾收集器付出大量額外的工作。(不過,只要記數(shù)為零,引用記數(shù)方案通常會立刻釋放對象)。 因此,應(yīng)付對象被引用的數(shù)量在你的程序的整個(gè)生命周期里是一筆較小但卻一直持續(xù)的開銷。 一種簡單的但卻較慢 的垃圾收集技術(shù)就是引用記數(shù)( reference counting)。垃圾收集器重新安排了整個(gè)過程,這使得分配存貯空間的時(shí)候一種高速,無窮閑置的堆模式成為可能。 最終,當(dāng)你創(chuàng)建了足夠多的對象后,你會耗盡內(nèi)存。(當(dāng)然,記錄工作會有一點(diǎn)額外的開銷,但是完全不同于 C++里那種在堆放工場里為尋找沒被利用的存貯空間而付出的開銷。 這就意味著對象存貯空間的分配速度明顯加快。 舉個(gè)例子,你可以認(rèn)為 C++的堆就如同一個(gè)堆放的工場,在這個(gè)工 場里,每一個(gè)對象都立有的地皮占有權(quán)不久會被廢除無效,并且這塊地皮必須重新加以利用。不過,事實(shí)上垃圾收集器能夠深刻影響對象的加速創(chuàng)建。 all other blocks get their generation count bumped if they have been referenced from somewhere. This handles the normal case of lots of shortlived temporary objects. Periodically, a full sweep is made—large objects are still not copied (they just get their generation count bumped), and blocks containing small objects are copied and pacted. The JVM monitors the efficiency of garbage collection and if it bees a waste of time because all objects are longlived, then it switches to markand sweep. Similarly, the JVM keeps track of how successful markandsweep is, and if the heap starts to bee fragmented, it switches back to stopandcopy. This is where the “adaptive” part es in, so you end up with a mouthful: “Adaptive generational stopandcopy markand sweep.” There are a number of additional speedups possible in a JVM. An especially important one involves the operation of the loader and what is called a justintime (JIT) piler. A JIT piler partially or fully converts a program into native machine code so that it doesn’t need to be inter