深入理解JVM參考模板_第1頁
深入理解JVM參考模板_第2頁
深入理解JVM參考模板_第3頁
深入理解JVM參考模板_第4頁
深入理解JVM參考模板_第5頁
已閱讀5頁,還剩86頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、深入理解JVM1   Java技術(shù)與Java虛擬機說起Java,人們首先想到的是Java編程語言,然而事實上,Java是一種技術(shù),它由四方面組成: Java編程語言、Java類文件格式、Java虛擬機和Java應(yīng)用程序接口(Java API)。它們的關(guān)系如下圖所示:圖1   Java四個方面的關(guān)系運行期環(huán)境代表著Java平臺,開發(fā)人員編寫Java代碼(.java文件),然后將之編譯成字節(jié)碼(.class文件)。最后字節(jié)碼被裝入內(nèi)存,一旦字節(jié)碼進(jìn)入虛擬機,它就會被解釋器解釋執(zhí)行,或者是被即時代碼發(fā)生器有選擇的轉(zhuǎn)換成機器碼執(zhí)行。從上圖也可以看出Java平臺由

2、Java虛擬機和 Java應(yīng)用程序接口搭建,Java語言則是進(jìn)入這個平臺的通道,用Java語言編寫并編譯的程序可以運行在這個平臺上。這個平臺的結(jié)構(gòu)如下圖所示:1 / 91在Java平臺的結(jié)構(gòu)中, 可以看出,Java虛擬機(JVM) 處在核心的位置,是程序與底層操作系統(tǒng)和硬件無關(guān)的關(guān)鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操作系統(tǒng), 其中依賴于平臺的部分稱為適配器;JVM 通過移植接口在具體的平臺和操作系統(tǒng)上實現(xiàn);在JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應(yīng)用程序(application) 和小程序(Java applet)

3、 可以在任何Java平臺上運行而無需考慮底層平臺, 就是因為有Java虛擬機(JVM)實現(xiàn)了程序與操作系統(tǒng)的分離,從而實現(xiàn)了Java 的平臺無關(guān)性。那么到底什么是Java虛擬機(JVM)呢?通常我們談?wù)揓VM時,我們的意思可能是:1. 對JVM規(guī)范的的比較抽象的說明;2. 對JVM的具體實現(xiàn);3. 在程序運行期間所生成的一個JVM實例。對JVM規(guī)范的的抽象說明是一些概念的集合,它們已經(jīng)在書The Java Virtual Machine Specification(Java虛擬機規(guī)范)中被詳細(xì)地描述了;對JVM的具體實現(xiàn)要么是軟件,要么是軟件和硬件的組合,它已經(jīng)被許多生產(chǎn)廠商所實現(xiàn),并存在于多

4、種平臺之上;運行Java程序的任務(wù)由JVM的運行期實例單個承擔(dān)。在本文中我們所討論的Java虛擬機(JVM)主要針對第三種情況而言。它可以被看成一個想象中的機器,在實際的計算機上通過軟件模擬來實現(xiàn),有自己想象中的硬件,如處理器、堆棧、寄存器等,還有自己相應(yīng)的指令系統(tǒng)。JVM在它的生存周期中有一個明確的任務(wù),那就是運行Java程序,因此當(dāng)Java程序啟動的時候,就產(chǎn)生JVM的一個實例;當(dāng)程序運行結(jié)束的時候,該實例也跟著消失了。下面我們從JVM的體系結(jié)構(gòu)和它的運行過程這兩個方面來對它進(jìn)行比較深入的研究。2   Java虛擬機的體系結(jié)構(gòu)剛才已經(jīng)提到,JVM可以由不同的廠商來實現(xiàn)。

5、由于廠商的不同必然導(dǎo)致JVM在實現(xiàn)上的一些不同,然而JVM還是可以實現(xiàn)跨平臺的特性,這就要歸功于設(shè)計JVM時的體系結(jié)構(gòu)了。我們知道,一個JVM實例的行為不光是它自己的事,還涉及到它的子系統(tǒng)、存儲區(qū)域、數(shù)據(jù)類型和指令這些部分,它們描述了JVM的一個抽象的內(nèi)部體系結(jié)構(gòu),其目的不光規(guī)定實現(xiàn)JVM時它內(nèi)部的體系結(jié)構(gòu),更重要的是提供了一種方式,用于嚴(yán)格定義實現(xiàn)時的外部行為。每個JVM都有兩種機制,一個是裝載具有合適名稱的類(類或是接口),叫做類裝載子系統(tǒng);另外的一個負(fù)責(zé)執(zhí)行包含在已裝載的類或接口中的指令,叫做運行引擎。每個JVM又包括方法區(qū)、堆、 Java棧、程序計數(shù)器和本地方法棧這五個部分,這幾個部分

6、和類裝載機制與運行引擎機制一起組成的體系結(jié)構(gòu)圖為:圖3   JVM的體系結(jié)構(gòu)JVM的每個實例都有一個它自己的方法域和一個堆,運行于JVM內(nèi)的所有的線程都共享這些區(qū)域;當(dāng)虛擬機裝載類文件的時候,它解析其中的二進(jìn)制數(shù)據(jù)所包含的類信息,并把它們放到方法域中;當(dāng)程序運行的時候,JVM把程序初始化的所有對象置于堆上;而每個線程創(chuàng)建的時候,都會擁有自己的程序計數(shù)器和 Java棧,其中程序計數(shù)器中的值指向下一條即將被執(zhí)行的指令,線程的Java棧則存儲為該線程調(diào)用Java方法的狀態(tài);本地方法調(diào)用的狀態(tài)被存儲在本地方法棧,該方法棧依賴于具體的實現(xiàn)。下面分別對這幾個部分進(jìn)行說明。執(zhí)行引擎處于J

7、VM的核心位置,在Java虛擬機規(guī)范中,它的行為是由指令集所決定的。盡管對于每條指令,規(guī)范很詳細(xì)地說明了當(dāng)JVM執(zhí)行字節(jié)碼遇到指令時,它的實現(xiàn)應(yīng)該做什么,但對于怎么做卻言之甚少。Java虛擬機支持大約248個字節(jié)碼。每個字節(jié)碼執(zhí)行一種基本的CPU運算,例如,把一個整數(shù)加到寄存器,子程序轉(zhuǎn)移等。Java指令集相當(dāng)于Java程序的匯編語言。Java指令集中的指令包含一個單字節(jié)的操作符,用于指定要執(zhí)行的操作,還有0個或多個操作數(shù),提供操作所需的參數(shù)或數(shù)據(jù)。許多指令沒有操作數(shù),僅由一個單字節(jié)的操作符構(gòu)成。虛擬機的內(nèi)層循環(huán)的執(zhí)行過程如下: do 取一個操作符字節(jié); 根據(jù)操作符的值執(zhí)行一個動作; whil

8、e(程序未結(jié)束)由于指令系統(tǒng)的簡單性,使得虛擬機執(zhí)行的過程十分簡單,從而有利于提高執(zhí)行的效率。指令中操作數(shù)的數(shù)量和大小是由操作符決定的。如果操作數(shù)比一個字節(jié)大,那么它存儲的順序是高位字節(jié)優(yōu)先。例如,一個16位的參數(shù)存放時占用兩個字節(jié),其值為:第一個字節(jié)*256+第二個字節(jié)字節(jié)碼。指令流一般只是字節(jié)對齊的。指令tableswitch和lookup是例外,在這兩條指令內(nèi)部要求強制的4字節(jié)邊界對齊。對于本地方法接口,實現(xiàn)JVM并不要求一定要有它的支持,甚至可以完全沒有。Sun公司實現(xiàn)Java本地接口(JNI)是出于可移植性的考慮,當(dāng)然我們也可以設(shè)計出其它的本地接口來代替Sun公司的JNI。但是這些設(shè)

9、計與實現(xiàn)是比較復(fù)雜的事情,需要確保垃圾回收器不會將那些正在被本地方法調(diào)用的對象釋放掉。Java的堆是一個運行時數(shù)據(jù)區(qū),類的實例(對象)從中分配空間,它的管理是由垃圾回收來負(fù)責(zé)的:不給程序員顯式釋放對象的能力。Java不規(guī)定具體使用的垃圾回收算法,可以根據(jù)系統(tǒng)的需求使用各種各樣的算法。Java方法區(qū)與傳統(tǒng)語言中的編譯后代碼或是Unix進(jìn)程中的正文段類似。它保存方法代碼(編譯后的java代碼)和符號表。在當(dāng)前的Java實現(xiàn)中,方法代碼不包括在垃圾回收堆中,但計劃在將來的版本中實現(xiàn)。每個類文件包含了一個Java類或一個Java界面的編譯后的代碼。可以說類文件是 Java語言的執(zhí)行代碼文件。為了保證類

10、文件的平臺無關(guān)性,Java虛擬機規(guī)范中對類文件的格式也作了詳細(xì)的說明。其具體細(xì)節(jié)請參考Sun公司的Java 虛擬機規(guī)范。Java虛擬機的寄存器用于保存機器的運行狀態(tài),與微處理器中的某些專用寄存器類似。Java虛擬機的寄存器有四種:1. pc: Java程序計數(shù)器;2. optop: 指向操作數(shù)棧頂端的指針;3. frame: 指向當(dāng)前執(zhí)行方法的執(zhí)行環(huán)境的指針;。4. vars: 指向當(dāng)前執(zhí)行方法的局部變量區(qū)第一個變量的指針。在上述體系結(jié)構(gòu)圖中,我們所說的是第一種,即程序計數(shù)器,每個線程一旦被創(chuàng)建就擁有了自己的程序計數(shù)器。當(dāng)線程執(zhí)行Java方法的時候,它包含該線程正在被執(zhí)行的指令的地址。但是若線

11、程執(zhí)行的是一個本地的方法,那么程序計數(shù)器的值就不會被定義。Java虛擬機的棧有三個區(qū)域:局部變量區(qū)、運行環(huán)境區(qū)、操作數(shù)區(qū)。局部變量區(qū)每個Java方法使用一個固定大小的局部變量集。它們按照與vars寄存器的字偏移量來尋址。局部變量都是32位的。長整數(shù)和雙精度浮點數(shù)占據(jù)了兩個局部變量的空間,卻按照第一個局部變量的索引來尋址。(例如,一個具有索引n的局部變量,如果是一個雙精度浮點數(shù),那么它實際占據(jù)了索引n和n+1所代表的存儲空間)虛擬機規(guī)范并不要求在局部變量中的64位的值是64位對齊的。虛擬機提供了把局部變量中的值裝載到操作數(shù)棧的指令,也提供了把操作數(shù)棧中的值寫入局部變量的指令。運行環(huán)境區(qū)在運行環(huán)境

12、中包含的信息用于動態(tài)鏈接,正常的方法返回以及異常捕捉。動態(tài)鏈接運行環(huán)境包括對指向當(dāng)前類和當(dāng)前方法的解釋器符號表的指針,用于支持方法代碼的動態(tài)鏈接。方法的class文件代碼在引用要調(diào)用的方法和要訪問的變量時使用符號。動態(tài)鏈接把符號形式的方法調(diào)用翻譯成實際方法調(diào)用,裝載必要的類以解釋還沒有定義的符號,并把變量訪問翻譯成與這些變量運行時的存儲結(jié)構(gòu)相應(yīng)的偏移地址。動態(tài)鏈接方法和變量使得方法中使用的其它類的變化不會影響到本程序的代碼。正常的方法返回如果當(dāng)前方法正常地結(jié)束了,在執(zhí)行了一條具有正確類型的返回指令時,調(diào)用的方法會得到一個返回值。執(zhí)行環(huán)境在正常返回的情況下用于恢復(fù)調(diào)用者的寄存器,并把調(diào)用者的程序

13、計數(shù)器增加一個恰當(dāng)?shù)臄?shù)值,以跳過已執(zhí)行過的方法調(diào)用指令,然后在調(diào)用者的執(zhí)行環(huán)境中繼續(xù)執(zhí)行下去。異常捕捉異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:動態(tài)鏈接錯,如無法找到所需的class文件。運行時錯,如對一個空指針的引用。程序使用了throw語句。當(dāng)異常發(fā)生時,Java虛擬機采取如下措施:· 檢查與當(dāng)前方法相聯(lián)系的catch子句表。每個catch子句包含其有效指令范圍,能夠處理的異常類型,以及處理異常的代碼塊地址。· 與異常相匹配的catch子句應(yīng)該符合下面的條件:造成異常的指令在其指令范圍之內(nèi),

14、發(fā)生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那么系統(tǒng)轉(zhuǎn)移到指定的異常處理塊處執(zhí)行;如果沒有找到異常處理塊,重復(fù)尋找匹配的catch子句的過程,直到當(dāng)前方法的所有嵌套的 catch子句都被檢查過。· 由于虛擬機從第一個匹配的catch子句處繼續(xù)執(zhí)行,所以catch子句表中的順序是很重要的。因為Java代碼是結(jié)構(gòu)化的,因此總可以把某個方法的所有的異常處理器都按序排列到一個表中,對任意可能的程序計數(shù)器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計數(shù)器值下發(fā)生的異常情況。· 如果找不到匹配的catch子句,那么當(dāng)前方法得到一個&quo

15、t;未截獲異常"的結(jié)果并返回到當(dāng)前方法的調(diào)用者,好像異常剛剛在其調(diào)用者中發(fā)生一樣。如果在調(diào)用者中仍然沒有找到相應(yīng)的異常處理塊,那么這種錯誤將被傳播下去。如果錯誤被傳播到最頂層,那么系統(tǒng)將調(diào)用一個缺省的異常處理塊。操作數(shù)棧區(qū)機器指令只從操作數(shù)棧中取操作數(shù),對它們進(jìn)行操作,并把結(jié)果返回到棧中。選擇棧結(jié)構(gòu)的原因是:在只有少量寄存器或非通用寄存器的機器(如 Intel486)上,也能夠高效地模擬虛擬機的行為。操作數(shù)棧是32位的。它用于給方法傳遞參數(shù),并從方法接收結(jié)果,也用于支持操作的參數(shù),并保存操作的結(jié)果。例如,iadd指令將兩個整數(shù)相加。相加的兩個整數(shù)應(yīng)該是操作數(shù)棧頂?shù)膬蓚€字。這兩個字是由

16、先前的指令壓進(jìn)堆棧的。這兩個整數(shù)將從堆棧彈出、相加,并把結(jié)果壓回到操作數(shù)棧中。每個原始數(shù)據(jù)類型都有專門的指令對它們進(jìn)行必須的操作。每個操作數(shù)在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。操作數(shù)只能被適用于其類型的操作符所操作。例如,壓入兩個int類型的數(shù),如果把它們當(dāng)作是一個long類型的數(shù)則是非法的。在Sun的虛擬機實現(xiàn)中,這個限制由字節(jié)碼驗證器強制實行。但是,有少數(shù)操作(操作符dupe和swap),用于對運行時數(shù)據(jù)區(qū)進(jìn)行操作時是不考慮類型的。本地方法棧,當(dāng)一個線程調(diào)用本地方法時,它就不再受到虛擬機關(guān)于結(jié)構(gòu)和安全限制方面的約束,它既可以訪問虛擬機的運行期數(shù)據(jù)區(qū),也可

17、以使用本地處理器以及任何類型的棧。例如,本地棧是一個C語言的棧,那么當(dāng)C程序調(diào)用C函數(shù)時,函數(shù)的參數(shù)以某種順序被壓入棧,結(jié)果則返回給調(diào)用函數(shù)。在實現(xiàn)Java虛擬機時,本地方法接口使用的是C語言的模型棧,那么它的本地方法棧的調(diào)度與使用則完全與C語言的棧相同。3   Java虛擬機的運行過程上面對虛擬機的各個部分進(jìn)行了比較詳細(xì)的說明,下面通過一個具體的例子來分析它的運行過程。虛擬機通過調(diào)用某個指定類的方法main啟動,傳遞給main一個字符串?dāng)?shù)組參數(shù),使指定的類被裝載,同時鏈接該類所使用的其它的類型,并且初始化它們。例如對于程序:class HelloApp public st

18、atic void main(String args) System.out.println("Hello World!"); for (int i = 0; i < args.length; i+ ) System.out.println(argsi); 編譯后在命令行模式下鍵入: java HelloApp run virtual machine將通過調(diào)用HelloApp的方法main來啟動java虛擬機,傳遞給main一個包含三個字符串"run"、"virtual"、"machine"的數(shù)組?,F(xiàn)在我們略

19、述虛擬機在執(zhí)行HelloApp時可能采取的步驟。開始試圖執(zhí)行類HelloApp的main方法,發(fā)現(xiàn)該類并沒有被裝載,也就是說虛擬機當(dāng)前不包含該類的二進(jìn)制代表,于是虛擬機使用 ClassLoader試圖尋找這樣的二進(jìn)制代表。如果這個進(jìn)程失敗,則拋出一個異常。類被裝載后同時在main方法被調(diào)用之前,必須對類 HelloApp與其它類型進(jìn)行鏈接然后初始化。鏈接包含三個階段:檢驗,準(zhǔn)備和解析。檢驗檢查被裝載的主類的符號和語義,準(zhǔn)備則創(chuàng)建類或接口的靜態(tài)域以及把這些域初始化為標(biāo)準(zhǔn)的默認(rèn)值,解析負(fù)責(zé)檢查主類對其它類或接口的符號引用,在這一步它是可選的。類的初始化是對類中聲明的靜態(tài)初始化函數(shù)和靜態(tài)域的初始化構(gòu)

20、造方法的執(zhí)行。一個類在初始化之前它的父類必須被初始化。整個過程如下:圖4:虛擬機的運行過程4   結(jié)束語本文通過對JVM的體系結(jié)構(gòu)的深入研究以及一個Java程序執(zhí)行時虛擬機的運行過程的詳細(xì)分析,意在剖析清楚Java虛擬機的機理。慢慢琢磨JVM1 JVM簡介JVM是我們Javaer的最基本功底了,剛開始學(xué)Java的時候,一般都是從“Hello World”開始的,然后會寫個復(fù)雜點class,然后再找一些開源框架,比如Spring,Hibernate等等,再然后就開發(fā)企業(yè)級的應(yīng)用,比如網(wǎng)站、企業(yè)內(nèi)部應(yīng)用、實時交易系統(tǒng)等等,直到某一天突然發(fā)現(xiàn)做的系統(tǒng)咋就這么慢呢,而且時不時還來個

21、內(nèi)存溢出什么的,今天是交易系統(tǒng)報了StackOverflowError,明天是網(wǎng)站系統(tǒng)報了個OutOfMemoryError,這種錯誤又很難重現(xiàn),只有分析Javacore和dump文件,運氣好點還能分析出個結(jié)果,運行遭的點,就直接去廟里燒香吧!每天接客戶的電話都是戰(zhàn)戰(zhàn)兢兢的,生怕再出什么幺蛾子了。我想Java做的久一點的都有這樣的經(jīng)歷,那這些問題的最終根結(jié)是在哪呢? JVM。JVM全稱是Java Virtual Machine,Java虛擬機,也就是在計算機上再虛擬一個計算機,這和我們使用 VMWare不一樣,那個虛擬的東西你是可以看到的,這個JVM你是看不到的,它存在內(nèi)存

22、中。我們知道計算機的基本構(gòu)成是:運算器、控制器、存儲器、輸入和輸出設(shè)備,那這個JVM也是有這成套的元素,運算器是當(dāng)然是交給硬件CPU還處理了,只是為了適應(yīng)“一次編譯,隨處運行”的情況,需要做一個翻譯動作,于是就用了JVM自己的命令集,這與匯編的命令集有點類似,每一種匯編命令集針對一個系列的CPU,比如8086系列的匯編也是可以用在8088上的,但是就不能跑在8051上,而JVM的命令集則是可以到處運行的,因為JVM做了翻譯,根據(jù)不同的CPU,翻譯成不同的機器語言。JVM中我們最需要深入理解的就是它的存儲部分,存儲?硬盤?NO,NO, JVM是一個內(nèi)存中的虛擬機,那它的存儲就是內(nèi)存了,

23、我們寫的所有類、常量、變量、方法都在內(nèi)存中,這決定著我們程序運行的是否健壯、是否高效,接下來的部分就是重點介紹之。2 JVM的組成部分我們先把JVM這個虛擬機畫出來,如下圖所示:  從這個圖中可以看到,JVM是運行在操作系統(tǒng)之上的,它與硬件沒有直接的交互。我們再來看下JVM有哪些組成部分,如下圖所示: 該圖參考了網(wǎng)上廣為流傳的JVM構(gòu)成圖,大家看這個圖,整個JVM分為四部分:q  Class Loader 類加載器類加載器的作用是加載類文件到內(nèi)存,比如編寫一個HelloWord.java程序,然后通過javac編譯成class文件,那

24、怎么才能加載到內(nèi)存中被執(zhí)行呢?Class Loader承擔(dān)的就是這個責(zé)任,那不可能隨便建立一個.class文件就能被加載的,Class Loader加載的class文件是有格式要求,在JVM Specification中式這樣定義Class文件的結(jié)構(gòu):    ClassFile       u4 magic;      u2 minor_version;      u2 maj

25、or_version;      u2 constant_pool_count;      cp_info constant_poolconstant_pool_count-1;      u2 access_flags;      u2 this_class;      u2 super_clas

26、s;      u2 interfaces_count;      u2 interfacesinterfaces_count;      u2 fields_count;      field_info fieldsfields_count;      u2 methods_count; 

27、;     method_info methodsmethods_count;      u2 attributes_count;      attribute_info attributesattributes_count;    需要詳細(xì)了解的話,可以仔細(xì)閱讀JVM Specification的第四章“The class File Format”,這里不再詳細(xì)說明。友情提示:Clas

28、s Loader只管加載,只要符合文件結(jié)構(gòu)就加載,至于說能不能運行,則不是它負(fù)責(zé)的,那是由Execution Engine負(fù)責(zé)的。q  Execution Engine 執(zhí)行引擎執(zhí)行引擎也叫做解釋器(Interpreter),負(fù)責(zé)解釋命令,提交操作系統(tǒng)執(zhí)行。q  Native Interface本地接口本地接口的作用是融合不同的編程語言為Java所用,它的初衷是融合C/C+程序,Java誕生的時候是C/C+橫行的時候,要想立足,必須有一個聰明的、睿智的調(diào)用C/C+程序,于是就在內(nèi)存中專門開辟了一塊區(qū)域處理標(biāo)記為native的代碼,它的具體做法是

29、Native Method Stack中登記native方法,在Execution Engine執(zhí)行時加載native libraies。目前該方法使用的是越來越少了,除非是與硬件有關(guān)的應(yīng)用,比如通過Java程序驅(qū)動打印機,或者Java系統(tǒng)管理生產(chǎn)設(shè)備,在企業(yè)級應(yīng)用中已經(jīng)比較少見,因為現(xiàn)在的異構(gòu)領(lǐng)域間的通信很發(fā)達(dá),比如可以使用Socket通信,也可以使用Web Service等等,不多做介紹。q  Runtime data area運行數(shù)據(jù)區(qū)運行數(shù)據(jù)區(qū)是整個JVM的重點。我們所有寫的程序都被加載到這里,之后才開始運行,Java生態(tài)系統(tǒng)如此的繁榮,得益于該區(qū)域的優(yōu)良自治,下一

30、章節(jié)詳細(xì)介紹之。 整個JVM框架由加載器加載文件,然后執(zhí)行器在內(nèi)存中處理數(shù)據(jù),需要與異構(gòu)系統(tǒng)交互是可以通過本地接口進(jìn)行,瞧,一個完整的系統(tǒng)誕生了!2 JVM的內(nèi)存管理所有的數(shù)據(jù)和程序都是在運行數(shù)據(jù)區(qū)存放,它包括以下幾部分:q  Stack 棧棧也叫棧內(nèi)存,是Java程序的運行區(qū),是在線程創(chuàng)建時創(chuàng)建,它的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對于棧來說不存在垃圾回收問題,只要線程一結(jié)束,該棧就Over。問題出來了:棧中存的是那些數(shù)據(jù)呢?又什么是格式呢?棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在,棧幀是一個內(nèi)存區(qū)塊,是一個數(shù)據(jù)集,是

31、一個有關(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棧幀,遵循“先進(jìn)后出”原則。那棧幀中到底存在著什么數(shù)據(jù)呢?棧幀中主要保存3類數(shù)據(jù):本地變量(Local Variables),包括輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;棧操作(Operand Stack),記錄出棧、入棧的操作;棧幀數(shù)據(jù)(Frame Data),包括類文件、方法等等。光說比較枯燥,我們畫個圖來理解一下Java棧,如下圖所示: 圖示在一個棧中有兩個棧幀,棧幀2是最先被調(diào)用的方法,

32、先入棧,然后方法2又調(diào)用了方法1,棧幀1處于棧頂?shù)奈恢茫瑮?處于棧底,執(zhí)行完畢后,依次彈出棧幀1和棧幀2,線程結(jié)束,棧釋放。q  Heap 堆內(nèi)存一個JVM實例只存在一個堆類存,堆內(nèi)存的大小是可以調(diào)節(jié)的。類加載器讀取了類文件后,需要把類、方法、常變量放到堆內(nèi)存中,以方便執(zhí)行器執(zhí)行,堆內(nèi)存分為三部分:Permanent Space 永久存儲區(qū)永久存儲區(qū)是一個常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的Class,Interface的元數(shù)據(jù),也就是說它存儲的是運行環(huán)境必須的類信息,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會被垃圾回收器回收掉的,關(guān)閉JVM才會釋放此區(qū)域所占用的

33、內(nèi)存。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ū)進(jìn)行垃圾回收,將伊甸園區(qū)中的不再被其他對象所引用的對象進(jìn)行銷毀。然后將伊甸園中的剩余對象移動到幸存0區(qū)。若幸存0區(qū)也滿了,再對該區(qū)

34、進(jìn)行垃圾回收,然后移動到1區(qū)。那如果1區(qū)也滿了呢?再移動到養(yǎng)老區(qū)。Tenure generation space養(yǎng)老區(qū)養(yǎng)老區(qū)用于保存從新生區(qū)篩選出來的JAVA對象,一般池對象都在這個區(qū)域活躍。   三個區(qū)的示意圖如下: q  Method Area 方法區(qū)方法區(qū)是被所有線程共享,該區(qū)域保存所有字段和方法字節(jié)碼,以及一些特殊方法如構(gòu)造函數(shù),接口代碼也在此定義。q  PC Register 程序計數(shù)器每個線程都有一個程序計數(shù)器,就是一個指針,指向方法區(qū)中的方法字節(jié)碼,由執(zhí)行引擎讀取下一條指令。q&#

35、160; Native Method Stack 本地方法棧   3 JVM相關(guān)問題問:堆和棧有什么區(qū)別答:堆是存放對象的,但是對象內(nèi)的臨時變量是存在棧內(nèi)存中,如例子中的methodVar是在運行期存放到棧中的。棧是跟隨線程的,有線程就有棧,堆是跟隨JVM的,有JVM就有堆內(nèi)存。 問:堆內(nèi)存中到底存在著什么東西?答:對象,包括對象變量以及對象方法。 問:類變量和實例變量有什么區(qū)別?答:靜態(tài)變量是類變量,非靜態(tài)變量是實例變量,直白的說,有static修飾的變量是靜態(tài)變量,沒有static修飾的變量是實例變量。靜態(tài)變量存在方法區(qū)

36、中,實例變量存在堆內(nèi)存中。 問:我聽說類變量是在JVM啟動時就初始化好的,和你這說的不同呀!答:那你是道聽途說,信我的,沒錯。 問:Java的方法(函數(shù))到底是傳值還是傳址?答:都不是,是以傳值的方式傳遞地址,具體的說原生數(shù)據(jù)類型傳遞的值,引用類型傳遞的地址。對于原始數(shù)據(jù)類型,JVM的處理方法是從Method Area或Heap中拷貝到Stack,然后運行frame中的方法,運行完畢后再把變量指拷貝回去。 問:為什么會產(chǎn)生OutOfMemory產(chǎn)生?答:一句話:Heap內(nèi)存中沒有足夠的可用內(nèi)存了。這句話要好好理解,不是說Heap沒有內(nèi)存了,是說新申請內(nèi)存的對象大于

37、Heap空閑內(nèi)存,比如現(xiàn)在Heap還空閑1M,但是新申請的內(nèi)存需要1.1M,于是就會報OutOfMemory了,可能以后的對象申請的內(nèi)存都只要0.9M,于是就只出現(xiàn)一次OutOfMemory,GC也正常了,看起來像偶發(fā)事件,就是這么回事。       但如果此時GC沒有回收就會產(chǎn)生掛起情況,系統(tǒng)不響應(yīng)了。 問:我產(chǎn)生的對象不多呀,為什么還會產(chǎn)生OutOfMemory?答:你繼承層次忒多了,Heap中 產(chǎn)生的對象是先產(chǎn)生 父類,然后才產(chǎn)生子類,明白不? 問:OutOfMemory錯誤分幾

38、種?答:分兩種,分別是“OutOfMemoryError:java heap size”和”O(jiān)utOfMemoryError: PermGen space”,兩種都是內(nèi)存溢出,heap size是說申請不到新的內(nèi)存了,這個很常見,檢查應(yīng)用或調(diào)整堆內(nèi)存大小。“PermGen space”是因為永久存儲區(qū)滿了,這個也很常見,一般在熱發(fā)布的環(huán)境中出現(xiàn),是因為每次發(fā)布應(yīng)用系統(tǒng)都不重啟,久而久之永久存儲區(qū)中的死對象太多導(dǎo)致新對象無法申請內(nèi)存,一般重新啟動一下即可。 問:為什么會產(chǎn)生StackOverflowError?答:因為一個線程把Stack內(nèi)存全部耗盡了,一般是遞歸函數(shù)造成的。 

39、;問:一個機器上可以看多個JVM嗎?JVM之間可以互訪嗎?答:可以多個JVM,只要機器承受得了。JVM之間是不可以互訪,你不能在A-JVM中訪問B-JVM的Heap內(nèi)存,這是不可能的。在以前老版本的JVM中,會出現(xiàn)A-JVM Crack后影響到B-JVM,現(xiàn)在版本非常少見。 問:為什么Java要采用垃圾回收機制,而不采用C/C+的顯式內(nèi)存管理?答:為了簡單,內(nèi)存管理不是每個程序員都能折騰好的。 問:為什么你沒有詳細(xì)介紹垃圾回收機制?答:垃圾回收機制每個JVM都不同,JVM Specification只是定義了要自動釋放內(nèi)存,也就是說它只定義了垃圾回收的抽象方法,具體怎么實現(xiàn)

40、各個廠商都不同,算法各異,這東西實在沒必要深入。 問:JVM中到底哪些區(qū)域是共享的?哪些是私有的?答:Heap和Method Area是共享的,其他都是私有的, 問:什么是JIT,你怎么沒說?答:JIT是指Just In Time,有的文檔把JIT作為JVM的一個部件來介紹,有的是作為執(zhí)行引擎的一部分來介紹,這都能理解。Java剛誕生的時候是一個解釋性語言,別噓,即使編譯成了字節(jié)碼(byte code)也是針對JVM的,它需要再次翻譯成原生代碼(native code)才能被機器執(zhí)行,于是效率的擔(dān)憂就提出來了。Sun為了解決該問題提出了一套新的機制,好,你想編譯成原生代碼,

41、沒問題,我在JVM上提供一個工具,把字節(jié)碼編譯成原生碼,下次你來訪問的時候直接訪問原生碼就成了,于是JIT就誕生了,就這么回事。 問:JVM還有哪些部分是你沒有提到的?答:JVM是一個異常復(fù)雜的東西,寫一本磚頭書都不為過,還有幾個要說明的:常量池(constant pool):按照順序存放程序中的常量,并且進(jìn)行索引編號的區(qū)域。比如int i =100,這個100就放在常量池中。安全管理器(Security Manager):提供Java運行期的安全控制,防止惡意攻擊,比如指定讀取文件,寫入文件權(quán)限,網(wǎng)絡(luò)訪問,創(chuàng)建進(jìn)程等等,Class Loader在Security Manager認(rèn)證

42、通過后才能加載class文件的。方法索引表(Methods table),記錄的是每個method的地址信息,Stack和Heap中的地址指針其實是指向Methods table地址。      問:為什么不建議在程序中顯式的生命System.gc()?答:因為顯式聲明是做堆內(nèi)存全掃描,也就是Full GC,是需要停止所有的活動的(Stop  The World Collection),你的應(yīng)用能承受這個嗎? 問:JVM有哪些調(diào)整參數(shù)?答:非常多,自己去找,堆內(nèi)存、棧內(nèi)存的大小都可以定義,甚至是堆內(nèi)存的三

43、個部分、新生代的各個比例都能調(diào)整。JVM內(nèi)存管理:深入Java內(nèi)存區(qū)域與OOMJava與C+之間有一堵由內(nèi)存動態(tài)分配和垃圾收集技術(shù)所圍成的高墻,墻外面的人想進(jìn)去,墻里面的人卻想出來。 概述:對于從事C、C+程序開發(fā)的開發(fā)人員來說,在內(nèi)存管理領(lǐng)域,他們即是擁有最高權(quán)力的皇帝又是執(zhí)行最基礎(chǔ)工作的勞動人民擁有每一個對象的“所有權(quán)”,又擔(dān)負(fù)著每一個對象生命開始到終結(jié)的維護(hù)責(zé)任。 對于Java程序員來說,不需要在為每一個new操作去寫配對的delete/free,不容易出現(xiàn)內(nèi)容泄漏和內(nèi)存溢出錯誤,看起來由JVM管理內(nèi)存一切都很美好。不過,也正是因為Java程序員把內(nèi)存控制的權(quán)力交給了

44、JVM,一旦出現(xiàn)泄漏和溢出,如果不了解JVM是怎樣使用內(nèi)存的,那排查錯誤將會是一件非常困難的事情。 VM運行時數(shù)據(jù)區(qū)域JVM執(zhí)行Java程序的過程中,會使用到各種數(shù)據(jù)區(qū)域,這些區(qū)域有各自的用途、創(chuàng)建和銷毀時間。根據(jù)Java虛擬機規(guī)范(第二版)(下文稱VM Spec)的規(guī)定,JVM包括下列幾個運行時數(shù)據(jù)區(qū)域: 1.程序計數(shù)器(Program Counter Register): 每一個Java線程都有一個程序計數(shù)器來用于保存程序執(zhí)行到當(dāng)前方法的哪一個指令,對于非Native方法,這個區(qū)域記錄的是正在執(zhí)行的VM原語的地址,如果正在執(zhí)行的是Natvie方法,這個區(qū)域則為

45、空(undefined)。此內(nèi)存區(qū)域是唯一一個在VM Spec中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。 2.Java虛擬機棧(Java Virtual Machine Stacks)與程序計數(shù)器一樣,VM棧的生命周期也是與線程相同。VM棧描述的是Java方法調(diào)用的內(nèi)存模型:每個方法被執(zhí)行的時候,都會同時創(chuàng)建一個幀(Frame)用于存儲本地變量表、操作棧、動態(tài)鏈接、方法出入口等信息。每一個方法的調(diào)用至完成,就意味著一個幀在VM棧中的入棧至出棧的過程。在后文中,我們將著重討論VM棧中本地變量表部分。經(jīng)常有人把Java內(nèi)存簡單的區(qū)分為堆內(nèi)存(Heap)和棧內(nèi)存(Stack

46、),實際中的區(qū)域遠(yuǎn)比這種觀點復(fù)雜,這樣劃分只是說明與變量定義密切相關(guān)的內(nèi)存區(qū)域是這兩塊。其中所指的“堆”后面會專門描述,而所指的“?!本褪荲M棧中各個幀的本地變量表部分。本地變量表存放了編譯期可知的各種標(biāo)量類型(boolean、byte、char、short、int、float、long、double)、對象引用(不是對象本身,僅僅是一個引用指針)、方法返回地址等。其中l(wèi)ong和double會占用2個本地變量空間(32bit),其余占用1個。本地變量表在進(jìn)入方法時進(jìn)行分配,當(dāng)進(jìn)入一個方法時,這個方法需要在幀中分配多大的本地變量是一件完全確定的事情,在方法運行期間不改變本地變量表的大小。在VM

47、Spec中對這個區(qū)域規(guī)定了2中異常狀況:如果線程請求的棧深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;如果VM??梢詣討B(tài)擴展(VM Spec中允許固定長度的VM棧),當(dāng)擴展時無法申請到足夠內(nèi)存則拋出OutOfMemoryError異常。3.本地方法棧(Native Method Stacks)本地方法棧與VM棧所發(fā)揮作用是類似的,只不過VM棧為虛擬機運行VM原語服務(wù),而本地方法棧是為虛擬機使用到的Native方法服務(wù)。它的實現(xiàn)的語言、方式與結(jié)構(gòu)并沒有強制規(guī)定,甚至有的虛擬機(譬如Sun Hotspot虛擬機)直接就把本地方法棧和VM棧合二為一。和VM棧一樣,這個區(qū)

48、域也會拋出StackOverflowError和OutOfMemoryError異常。4.Java堆(Java Heap)對于絕大多數(shù)應(yīng)用來說,Java堆是虛擬機管理最大的一塊內(nèi)存。Java堆是被所有線程共享的,在虛擬機啟動時創(chuàng)建。Java堆的唯一目的就是存放對象實例,絕大部分的對象實例都在這里分配。這一點在VM Spec中的描述是:所有的實例以及數(shù)組都在堆上分配(原文:The heap is the runtime data area from which memory for all class instances and arrays is allocated),但是在逃逸分析和標(biāo)量替換

49、優(yōu)化技術(shù)出現(xiàn)后,VM Spec的描述就顯得并不那么準(zhǔn)確了。Java堆內(nèi)還有更細(xì)致的劃分:新生代、老年代,再細(xì)致一點的:eden、from survivor、to survivor,甚至更細(xì)粒度的本地線程分配緩沖(TLAB)等,無論對Java堆如何劃分,目的都是為了更好的回收內(nèi)存,或者更快的分配內(nèi)存,在本章中我們僅僅針對內(nèi)存區(qū)域的作用進(jìn)行討論,Java堆中的上述各個區(qū)域的細(xì)節(jié),可參見本文第二章JVM內(nèi)存管理:深入垃圾收集器與內(nèi)存分配策略。根據(jù)VM Spec的要求,Java堆可以處于物理上不連續(xù)的內(nèi)存空間,它邏輯上是連續(xù)的即可,就像我們的磁盤空間一樣。實現(xiàn)時可以選擇實現(xiàn)成固定大小的,也可以是可擴展

50、的,不過當(dāng)前所有商業(yè)的虛擬機都是按照可擴展來實現(xiàn)的(通過-Xmx和-Xms控制)。如果在堆中無法分配內(nèi)存,并且堆也無法再擴展時,將會拋出OutOfMemoryError異常。5.方法區(qū)(Method Area)叫“方法區(qū)”可能認(rèn)識它的人還不太多,如果叫永久代(Permanent Generation)它的粉絲也許就多了。它還有個別名叫做Non-Heap(非堆),但是VM Spec上則描述方法區(qū)為堆的一個邏輯部分(原文:the method area is logically part of the heap),這個名字的問題還真容易令人產(chǎn)生誤解,我們在這里就不糾結(jié)了。方法區(qū)中存放了每個Clas

51、s的結(jié)構(gòu)信息,包括常量池、字段描述、方法描述等等。VM Space描述中對這個區(qū)域的限制非常寬松,除了和Java堆一樣不需要連續(xù)的內(nèi)存,也可以選擇固定大小或者可擴展外,甚至可以選擇不實現(xiàn)垃圾收集。相對來說,垃圾收集行為在這個區(qū)域是相對比較少發(fā)生的,但并不是某些描述那樣永久代不會發(fā)生GC(至少對當(dāng)前主流的商業(yè)JVM實現(xiàn)來說是如此),這里的GC主要是對常量池的回收和對類的卸載,雖然回收的“成績”一般也比較差強人意,尤其是類卸載,條件相當(dāng)苛刻。6.運行時常量池(Runtime Constant Pool)Class文件中除了有類的版本、字段、方法、接口等描述等信息外,還有一項信息是常量表(const

52、ant_pool table),用于存放編譯期已可知的常量,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)(永久代)存放。但是Java語言并不要求常量一定只有編譯期預(yù)置入Class的常量表的內(nèi)容才能進(jìn)入方法區(qū)常量池,運行期間也可將新內(nèi)容放入常量池(最典型的Sern()方法)。運行時常量池是方法區(qū)的一部分,自然受到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法在申請到內(nèi)存時會拋出OutOfMemoryError異常。 7.本機直接內(nèi)存(Direct Memory)直接內(nèi)存并不是虛擬機運行時數(shù)據(jù)區(qū)的一部分,它根本就是本機內(nèi)存而不是VM直接管理的區(qū)域。但是這部分內(nèi)存也會導(dǎo)致OutOfMemoryErr

53、or異常出現(xiàn),因此我們放到這里一起描述。在JDK1.4中新加入了NIO類,引入一種基于渠道與緩沖區(qū)的I/O方式,它可以通過本機Native函數(shù)庫直接分配本機內(nèi)存,然后通過一個存儲在Java堆里面的DirectByteBuffer對象作為這塊內(nèi)存的引用進(jìn)行操作。這樣能在一些場景中顯著提高性能,因為避免了在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ū)域總和

54、大于物理內(nèi)存限制(包括物理的和操作系統(tǒng)級的限制),而導(dǎo)致動態(tài)擴展時出現(xiàn)OutOfMemoryError異常。 實戰(zhàn)OutOfMemoryError上述區(qū)域中,除了程序計數(shù)器,其他在VM Spec中都描述了產(chǎn)生OutOfMemoryError(下稱OOM)的情形,那我們就實戰(zhàn)模擬一下,通過幾段簡單的代碼,令對應(yīng)的區(qū)域產(chǎn)生OOM異常以便加深認(rèn)識,同時初步介紹一些與內(nèi)存相關(guān)的虛擬機參數(shù)。下文的代碼都是基于Sun Hotspot虛擬機1.6版的實現(xiàn),對于不同公司的不同版本的虛擬機,參數(shù)與程序運行結(jié)果可能結(jié)果會有所差別。 Java堆 Java堆存放的是對象實例,因此只要不斷

55、建立對象,并且保證GC Roots到對象之間有可達(dá)路徑即可產(chǎn)生OOM異常。測試中限制Java堆大小為20M,不可擴展,通過參數(shù)-XX:+HeapDumpOnOutOfMemoryError讓虛擬機在出現(xiàn)OOM異常的時候Dump出內(nèi)存映像以便分析。(關(guān)于Dump映像文件分析方面的內(nèi)容,可參見本文第三章JVM內(nèi)存管理:深入JVM內(nèi)存異常分析與調(diào)優(yōu)。)清單1:Java堆OOM測試/* * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * author zzm */public class HeapOOM

56、        static class OOMObject                public static void main(String args)               List<OOMObject&

57、gt; list = new ArrayList<OOMObject>();               while (true)                      list.add(new OOMObject();&#

58、160;                     運行結(jié)果:java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid3404.hprof .Heap dump file created 22045981 bytes in 0.663 secs  VM棧和本地方法棧 Hotspot虛

59、擬機并不區(qū)分VM棧和本地方法棧,因此-Xoss參數(shù)實際上是無效的,棧容量只由-Xss參數(shù)設(shè)定。關(guān)于VM棧和本地方法棧在VM Spec描述了兩種異常:StackOverflowError與OutOfMemoryError,當(dāng)棧空間無法繼續(xù)分配分配時,到底是內(nèi)存太小還是棧太大其實某種意義上是對同一件事情的兩種描述而已,在筆者的實驗中,對于單線程應(yīng)用嘗試下面3種方法均無法讓虛擬機產(chǎn)生OOM,全部嘗試結(jié)果都是獲得SOF異常。 1.使用-Xss參數(shù)削減棧內(nèi)存容量。結(jié)果:拋出SOF異常時的堆棧深度相應(yīng)縮小。2.定義大量的本地變量,增大此方法對應(yīng)幀的長度。結(jié)果:拋出SOF異常時的堆棧深度相應(yīng)縮小。

60、3.創(chuàng)建幾個定義很多本地變量的復(fù)雜對象,打開逃逸分析和標(biāo)量替換選項,使得JIT編譯器允許對象拆分后在棧中分配。結(jié)果:實際效果同第二點。 清單2:VM棧和本地方法棧OOM測試(僅作為第1點測試程序)/* * VM Args:-Xss128k * author zzm */public class JavaVMStackSOF         private int stackLength = 1;       

61、; public void stackLeak()               stackLength+;              stackLeak();            

62、0;  public static void main(String args) throws Throwable               JavaVMStackSOF oom = new JavaVMStackSOF();              try    

63、                  oom.stackLeak();               catch (Throwable e)             

64、;         System.out.println("stack length:" + oom.stackLength);                     throw e;        

65、;              運行結(jié)果:stack length:2402Exception in thread "main" java.lang.StackOverflowError        at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:20)  

66、;      at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21)        at org.fenixsoft.oom.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:21) 如果在多線程環(huán)境下,不斷建立線程倒是可以產(chǎn)生OOM異常,但是基本上這個異常和VM??臻g夠不夠關(guān)系沒有直接關(guān)系,甚至是給每個線程的VM棧分配的內(nèi)

67、存越多反而越容易產(chǎn)生這個OOM異常。 原因其實很好理解,操作系統(tǒng)分配給每個進(jìn)程的內(nèi)存是有限制的,譬如32位Windows限制為2G,Java堆和方法區(qū)的大小JVM有參數(shù)可以限制最大值,那剩余的內(nèi)存為2G(操作系統(tǒng)限制)-Xmx(最大堆)-MaxPermSize(最大方法區(qū)),程序計數(shù)器消耗內(nèi)存很小,可以忽略掉,那虛擬機進(jìn)程本身耗費的內(nèi)存不計算的話,剩下的內(nèi)存就供每一個線程的VM棧和本地方法棧瓜分了,那自然每個線程中VM棧分配內(nèi)存越多,就越容易把剩下的內(nèi)存耗盡。 清單3:創(chuàng)建線程導(dǎo)致OOM異常/* * VM Args:-Xss2M (這時候不妨設(shè)大些)&#

68、160;* author zzm */public class JavaVMStackOOM         private void dontStop()               while (true)              &

69、#160;               public void stackLeakByThread()               while (true)             

70、0;        Thread thread = new Thread(new Runnable()                             Override     &#

71、160;                      public void run()                         &#

72、160;          dontStop();                                               

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論