網(wǎng)站開發(fā)學(xué)習(xí)網(wǎng)站網(wǎng)站頁(yè)面禁止訪問
目錄
1. 棧
2. 堆
3. 方法區(qū)
4. 本地方法棧
5. 程序計(jì)數(shù)器
首先來看一下JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)有哪些。
1. 棧
在介紹JVM棧之前,先了解一下?棧幀?概念。
棧幀:一個(gè)棧幀隨著一個(gè)方法的調(diào)用開始而創(chuàng)建,這個(gè)方法調(diào)用完成而銷毀。棧幀內(nèi)存放者方法中的局部變量,操作數(shù)棧等數(shù)據(jù)。
Java棧也稱作虛擬機(jī)棧(Java Vitual Machine Stack),JVM棧只對(duì)棧幀進(jìn)行存儲(chǔ),壓棧和出棧操作。
Java棧是Java方法執(zhí)行的內(nèi)存模型。下面我們來看一個(gè)Java棧圖。
Java棧中存放的是一個(gè)個(gè)的棧幀,每個(gè)棧幀對(duì)應(yīng)一個(gè)被調(diào)用的方法,在棧幀中包括局部變量表(Local Variables)、操作數(shù)棧(Operand Stack)、指向當(dāng)前方法所屬的類的運(yùn)行時(shí)常量池(運(yùn)行時(shí)常量池的概念在方法區(qū)部分會(huì)談到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些額外的附加信息。
當(dāng)線程執(zhí)行一個(gè)方法時(shí),就會(huì)隨之創(chuàng)建一個(gè)對(duì)應(yīng)的棧幀,并將建立的棧幀壓棧。當(dāng)方法執(zhí)行完畢之后,便會(huì)將棧幀出棧。
因此可知,線程當(dāng)前執(zhí)行的方法所對(duì)應(yīng)的棧幀必定位于Java棧的頂部。
對(duì)于所有的程序設(shè)計(jì)語(yǔ)言來說,棧這部分空間對(duì)程序員來說是不透明的。?
棧內(nèi)存的大小可以有兩種設(shè)置,固定值和根據(jù)線程需要?jiǎng)討B(tài)增長(zhǎng)。
在JVM棧這個(gè)數(shù)據(jù)區(qū)可能會(huì)發(fā)生拋出兩種錯(cuò)誤。
- StackOverflowError 出現(xiàn)在棧內(nèi)存設(shè)置成固定值的時(shí)候,當(dāng)程序執(zhí)行需要的棧內(nèi)存超過設(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ò)誤。
總結(jié)
- 每個(gè)線程包含一個(gè)棧區(qū),棧中只保存基礎(chǔ)數(shù)據(jù)類型的對(duì)象和自定義對(duì)象的引用(不是對(duì)象)。對(duì)象都存放在堆區(qū)中。
- 每個(gè)棧中的數(shù)據(jù)(基礎(chǔ)數(shù)據(jù)類型和對(duì)象引用)都是私有的,其他棧不能訪問。
- 棧分為3個(gè)部分:基本類型變量,執(zhí)行環(huán)境上下文,操作指令區(qū)(存放操作指令).
- 在函數(shù)中定義的一些基本類型的變量數(shù)據(jù)和對(duì)象的引用變量都在函數(shù)的棧內(nèi)存中分配。
- 當(dāng)在一段代碼塊定義一個(gè)變量時(shí),Java就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)該變量退出該作用域后,Java會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間,該內(nèi)存空間可以立即被另作他用。
2. 堆
堆數(shù)據(jù)區(qū)是用來存放對(duì)象和數(shù)組(特殊的對(duì)象)。堆內(nèi)存由多個(gè)線程共享。堆內(nèi)存隨著JVM啟動(dòng)而創(chuàng)建。
眾所周知,Java中有一個(gè)很好的特性就是自動(dòng)垃圾回收。垃圾回收就操作這個(gè)數(shù)據(jù)區(qū)來回收對(duì)象進(jìn)而釋放內(nèi)存。
如果堆內(nèi)存剩余的內(nèi)存不足以滿足于對(duì)象創(chuàng)建,JVM會(huì)拋出OutOfMemoryError錯(cuò)誤。
總結(jié)
- 存儲(chǔ)的全部是對(duì)象,每個(gè)對(duì)象包含一個(gè)與之對(duì)應(yīng)的class信息–class的目的是得到操作指令。
- jvm只有一個(gè)堆區(qū)(heap)被所有線程共享,堆區(qū)中不存放基本類型和對(duì)象引用,只存放對(duì)象本身。
- 堆的優(yōu)勢(shì)是可以動(dòng)態(tài)地分配內(nèi)存大小,生存期也不必事先告訴編譯器,因?yàn)樗窃谶\(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存的,Java的垃圾收集器會(huì)自動(dòng)收走這些不再使用的數(shù)據(jù)。
- 缺點(diǎn)是,由于要在運(yùn)行時(shí)動(dòng)態(tài)分配內(nèi)存,存取速度較慢。
3. 方法區(qū)
方法區(qū)在JVM中也是一個(gè)非常重要的區(qū)域,它與堆一樣,是被線程共享的區(qū)域。
在方法區(qū)中,存儲(chǔ)了每個(gè)類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。
在Class文件中除了類的字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池,用來存儲(chǔ)編譯期間生成的字面量和符號(hào)引用。
在方法區(qū)中有一個(gè)非常重要的部分就是運(yùn)行時(shí)常量池,它是每一個(gè)類或接口的常量池的運(yùn)行時(shí)表示形式,在類和接口被加載到JVM后,對(duì)應(yīng)的運(yùn)行時(shí)常量池就被創(chuàng)建出來。
當(dāng)然并非Class文件常量池中的內(nèi)容才能進(jìn)入運(yùn)行時(shí)常量池,在運(yùn)行期間也可將新的常量放入運(yùn)行時(shí)常量池中,比如String的intern方法。
4. 本地方法棧
一個(gè)支持native方法調(diào)用的JVM實(shí)現(xiàn),需要有這樣一個(gè)數(shù)據(jù)區(qū),就是本地方法棧。
Java官方對(duì)于本地方法的定義為methods written in a language other than the Java programming language,就是使用非Java語(yǔ)言實(shí)現(xiàn)的方法,但是通常我們指的一般為C或者C++,因此這個(gè)棧也有著C棧這一稱號(hào)。
一個(gè)不支持本地方法執(zhí)行的JVM沒有必要實(shí)現(xiàn)這個(gè)數(shù)據(jù)區(qū)域。
本地方法棧基本和JVM棧一樣,其大小也是可以設(shè)置為固定值或者動(dòng)態(tài)增加,因此也會(huì)對(duì)應(yīng)拋出StackOverflowError和OutOfMemoryError錯(cuò)誤。
在HotSopt虛擬機(jī)中直接就把本地方法棧和Java棧合二為一。
5. 程序計(jì)數(shù)器
在通用的計(jì)算機(jī)體系中,程序計(jì)數(shù)器用來記錄當(dāng)前正在執(zhí)行的指令,在JVM中也是如此。
程序計(jì)數(shù)器是線程私有,所以當(dāng)一個(gè)新的線程創(chuàng)建時(shí),程序計(jì)數(shù)器也會(huì)創(chuàng)建。
由于Java是支持多線程,Java中的程序計(jì)數(shù)器用來記錄當(dāng)前線程中正在執(zhí)行的指令。
如果當(dāng)前正在執(zhí)行的方法是本地方法,那么此刻程序計(jì)數(shù)器的值為undefined。
注意這個(gè)區(qū)域是唯一一個(gè)不拋出OutOfMemoryError的運(yùn)行時(shí)數(shù)據(jù)區(qū)。
?
參考自:https://blog.csdn.net/zhangqilugrubby/article/details/59110906