這篇文章主要講解了“java中JVM內(nèi)存模型的介紹”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java中JVM內(nèi)存模型的介紹”吧!
我們提供的服務(wù)有:成都做網(wǎng)站、成都網(wǎng)站制作、微信公眾號(hào)開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、陽泉ssl等。為近千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的陽泉網(wǎng)站制作公司
首先你應(yīng)該知道,運(yùn)行一個(gè) Java 應(yīng)用程序,我們必須要先安裝 JDK 或者 JRE 。這是因?yàn)?Java 應(yīng)用在編譯后會(huì)變成字節(jié)碼,然后通過字節(jié)碼運(yùn)行在 JVM 中,而 JVM 是 JRE 的核心組成部分。
JVM 不僅承擔(dān)了 Java 字節(jié)碼的分析(JIT compiler)和執(zhí)行(Runtime),同時(shí)也內(nèi)置了自動(dòng)內(nèi)存分配管理機(jī)制。這個(gè)機(jī)制可以大大降低手動(dòng)分配回收機(jī)制可能帶來的內(nèi)存泄露和內(nèi)存溢出風(fēng)險(xiǎn),使 Java 開發(fā)人員不需要關(guān)注每個(gè)對(duì)象的內(nèi)存分配以及回收,從而更專注于業(yè)務(wù)本身。
這個(gè)機(jī)制在提升 Java 開發(fā)效率的同時(shí),也容易使 Java 開發(fā)人員過度依賴于自動(dòng)化,弱化對(duì)內(nèi)存的管理能力,這樣系統(tǒng)就很容易發(fā)生 JVM 的堆內(nèi)存異常、垃圾回收(GC)的不合適以及 GC 次數(shù)過于頻繁等問題,這些都將直接影響到應(yīng)用服務(wù)的性能。
JVM 內(nèi)存模型共分為5個(gè)區(qū):堆(Heap)、方法區(qū)(Method Area)、程序計(jì)數(shù)器(Program Counter Register)、虛擬機(jī)棧(VM Stack)、本地方法棧(Native Method Stack)。

其中,堆(Heap)、方法區(qū)(Method Area)為線程共享,程序計(jì)數(shù)器(Program Counter Register)、虛擬機(jī)棧(VM Stack)、本地方法棧(Native Method Stack)為線程隔離。
堆是 JVM 內(nèi)存中最大的一塊內(nèi)存空間,該內(nèi)存被所有線程共享,幾乎所有對(duì)象和數(shù)組都被分配到了堆內(nèi)存中。
堆被劃分為新生代和老年代,新生代又被進(jìn)一步劃分為 Eden 區(qū)和 Survivor 區(qū),最后 Survivor 由 From Survivor 和 To Survivor 組成。
隨著 Java 版本的更新,其內(nèi)容又有了一些新的變化: >在 Java6 版本中,永久代在非堆內(nèi)存區(qū);到了 Java7 版本,永久代的靜態(tài)變量和運(yùn)行時(shí)常量池被合并到了堆中;而到了 Java8,永久代被元空間(處于本地內(nèi)存)取代了。

為什么要用元空間替換永久代呢?
為了融合 HotSpot JVM 與 JRockit VM,因?yàn)?JRockit 沒有永久代,所以不需要配置永久代。
永久代內(nèi)存經(jīng)常不夠用或發(fā)生內(nèi)存溢出(應(yīng)該是 JVM 中占用內(nèi)存最大的一塊),產(chǎn)生異常 java.lang.OutOfMemoryError: PermGen。在 JDK1.7 版本中,指定的 PermGen 區(qū)大小為 8M,由于 PermGen 中類的元數(shù)據(jù)信息在每次 FullGC 的時(shí)候都可能被收集,回收率都偏低,成績(jī)很難令人滿意;還有,為 PermGen 分配多大的空間很難確定,PermSize 的大小依賴于很多因素,比如,JVM 加載的 class 總數(shù)、常量池的大小和方法的大小等。
看到這兒,自然就想到了 GC 回收算法,不用急,我會(huì)在之后的文章中進(jìn)行講解,現(xiàn)在還是以 JVM 內(nèi)存模型為主。
什么是方法區(qū)? >方法區(qū)主要是用來存放已被虛擬機(jī)加載的類相關(guān)信息,包括類信息、常量池(字符串常量池以及所有基本類型都有其相應(yīng)的常量池)、運(yùn)行時(shí)常量池。這其中,類信息又包括了類的版本、字段、方法、接口和父類等信息。
JVM 在執(zhí)行某個(gè)類的時(shí)候,必須經(jīng)過加載、連接、初始化,而連接又包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段。
在加載類的時(shí)候,JVM 會(huì)先加載 class 文件,而在 class 文件中便有類的版本、字段、方法和接口等描述信息,這就是類信息。
在 class 文件中,除了類信息,還有一項(xiàng)信息是常量池 (Constant Pool Table),用于存放編譯期間生成的各種字面量和符號(hào)引用。
那字面量和符號(hào)引用又是什么呢?
字面量包括字符串(String a=“b”)、基本類型的常量(final 修飾的變量),符號(hào)引用則包括類和方法的全限定名(例如 String 這個(gè)類,它的全限定名就是 Java/lang/String)、字段的名稱和描述符以及方法的名稱和描述符。
當(dāng)類加載到內(nèi)存后,JVM 就會(huì)將 class 文件常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中;在解析階段,JVM 會(huì)把符號(hào)引用替換為直接引用(對(duì)象的索引值)。
例如: >類中的一個(gè)字符串常量在 class 文件中時(shí),存放在 class 文件常量池中的。 > >在 JVM 加載完類之后,JVM 會(huì)將這個(gè)字符串常量放到運(yùn)行時(shí)常量池中,并在解析階段,指定該字符串對(duì)象的索引值。
運(yùn)行時(shí)常量池是全局共享的,多個(gè)類共用一個(gè)運(yùn)行時(shí)常量池,因此,class 文件中常量池多個(gè)相同的字符串在運(yùn)行時(shí)常量池只會(huì)存在一份。
講到這里,大家是不是有些頭暈了,說實(shí)話,我在看到這些內(nèi)容的時(shí)候,也是云里霧里的,這里舉個(gè)例子幫助大家理解:
public static void main(String[] args) {
String str = "Hello";
System.out.println((str == ("Hel" + "lo")));
String loStr = "lo";
System.out.println((str == ("Hel" + loStr)));
System.out.println(str == ("Hel" + loStr).intern());
}其運(yùn)行結(jié)果為:
true false true
第一個(gè)為 true,是因?yàn)樵诰幾g成 class 文件時(shí),能夠識(shí)別為同一字符串的, JVM 會(huì)將其自動(dòng)優(yōu)化成字符串常量,引用自同一 String 對(duì)象。
第二個(gè)為 false,是因?yàn)樵谶\(yùn)行時(shí)創(chuàng)建的字符串具有獨(dú)立的內(nèi)存地址,所以不引用自同一 String 對(duì)象。
最后一個(gè)為 true,是因?yàn)?String 的 intern() 方法會(huì)查找在常量池中是否存在一個(gè)相等(調(diào)用 equals() 方法結(jié)果相等)的字符串,如果有則返回該字符串的引用,如果沒有則添加自己的字符串進(jìn)入常量池。
OutOfMemoryError出現(xiàn)在方法區(qū)無法滿足內(nèi)存分配需求的時(shí)候,比如一直往常量池中加入數(shù)據(jù),運(yùn)行時(shí)常量池就會(huì)溢出,從而報(bào)錯(cuò)。
程序計(jì)數(shù)器是一塊很小的內(nèi)存空間,主要用來記錄各個(gè)線程執(zhí)行的字節(jié)碼的地址,例如,分支、循環(huán)、跳轉(zhuǎn)、異常、線程恢復(fù)等都依賴于計(jì)數(shù)器。
由于 Java 是多線程語言,當(dāng)執(zhí)行的線程數(shù)量超過 CPU 數(shù)量時(shí),線程之間會(huì)根據(jù)時(shí)間片輪詢爭(zhēng)奪 CPU 資源。如果一個(gè)線程的時(shí)間片用完了,或者是其它原因?qū)е逻@個(gè)線程的 CPU 資源被提前搶奪,那么這個(gè)退出的線程就需要單獨(dú)的一個(gè)程序計(jì)數(shù)器,來記錄下一條運(yùn)行的指令。
由此可見,程序計(jì)數(shù)器和上下文切換有關(guān)。
>虛擬機(jī)棧是線程私有的內(nèi)存空間,它和 Java 線程一起創(chuàng)建。 > >當(dāng)創(chuàng)建一個(gè)線程時(shí),會(huì)在虛擬機(jī)棧中申請(qǐng)一個(gè)線程棧,用來保存方法的局部變量、操作數(shù)棧、動(dòng)態(tài)鏈接方法和返回地址等信息,并參與方法的調(diào)用和返回。 > >每一個(gè)方法的調(diào)用都伴隨著棧幀的入棧操作,方法的返回則是棧幀的出棧操作。
可以這么理解,虛擬機(jī)棧針對(duì)當(dāng)前 Java 應(yīng)用中所有線程,都有一個(gè)其相應(yīng)的線程棧,每一個(gè)線程棧都互相獨(dú)立、互不影響,里面存儲(chǔ)了該線程中獨(dú)有的信息。
StackOverflowError出現(xiàn)在棧內(nèi)存設(shè)置成固定值的時(shí)候,當(dāng)程序執(zhí)行需要的棧內(nèi)存超過設(shè)定的固定值時(shí)會(huì)拋出這個(gè)錯(cuò)誤。
OutOfMemoryError出現(xiàn)在棧內(nèi)存設(shè)置成動(dòng)態(tài)增長(zhǎng)的時(shí)候,當(dāng)JVM嘗試申請(qǐng)的內(nèi)存大小超過了其可用內(nèi)存時(shí)會(huì)拋出這個(gè)錯(cuò)誤。
>本地方法棧跟虛擬機(jī)棧的功能類似,虛擬機(jī)棧用于管理 Java 方法的調(diào)用,而本地方法棧則用于管理本地方法的調(diào)用。 > >但本地方法并不是用 Java 實(shí)現(xiàn)的,而是由 C 語言實(shí)現(xiàn)的。
也就是說,本地方法棧中并沒有我們寫的代碼邏輯,其由native修飾,由 C 語言實(shí)現(xiàn)。
感謝各位的閱讀,以上就是“java中JVM內(nèi)存模型的介紹”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)java中JVM內(nèi)存模型的介紹這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
新聞名稱:java中JVM內(nèi)存模型的介紹
分享網(wǎng)址:http://chinadenli.net/article30/ggjepo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供響應(yīng)式網(wǎng)站、做網(wǎng)站、小程序開發(fā)、App開發(fā)、服務(wù)器托管、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)