国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁 > news >正文

域名解析網(wǎng)站鄭州網(wǎng)絡(luò)公司

域名解析網(wǎng)站,鄭州網(wǎng)絡(luò)公司,深圳做網(wǎng)站商,上海手機網(wǎng)站JVM系列之:內(nèi)存與垃圾回收篇(一) ##本篇內(nèi)容概述: 1、JVM結(jié)構(gòu) 2、類加載子系統(tǒng) 3、運行時數(shù)據(jù)區(qū)之:PC寄存器、Java棧、本地方法棧一、JVM與JAVA體系結(jié)構(gòu) JAVA虛擬機與JAVA語言并沒有必然的聯(lián)系,它只是與特…

JVM系列之:內(nèi)存與垃圾回收篇(一)

##本篇內(nèi)容概述:
1、JVM結(jié)構(gòu)
2、類加載子系統(tǒng)
3、運行時數(shù)據(jù)區(qū)之:PC寄存器、Java棧、本地方法棧

一、JVM與JAVA體系結(jié)構(gòu)

JAVA虛擬機與JAVA語言并沒有必然的聯(lián)系,它只是與特定的二進制文件格式:Class文件格式關(guān)聯(lián),Class文件中包含了JAVA虛擬機指令集(字節(jié)碼、Bytecodes)和符號表,還有一些其他輔助信息。

1、JVM整體結(jié)構(gòu)

【說明:線程共享->方法區(qū)和堆; 各個線程獨享:Java棧、本地方法棧和程序計數(shù)器】

在這里插入圖片描述

【程序計數(shù)器Program Counter Register即:PC寄存器】

2、JAVA代碼執(zhí)行流程

在這里插入圖片描述

3、JVM的架構(gòu)模型

java編譯器輸入的指令流基本上基于以下兩種指令集架構(gòu):

  • 基于棧的指令集架構(gòu)(基于零地址指令,出棧入棧,指令集小,但指令多。不依賴于硬件可移植性好)
  • 基于寄存器的指令集架構(gòu)(以一地址、二地址、三地址指令為主,依賴于硬件可移植性差,但執(zhí)行效率更高)

總結(jié):

由于跨平臺性的設(shè)計,Java的指令都是根據(jù)棧來設(shè)計的。不同的平臺CPU架構(gòu)不同,所以不能設(shè)計為基于寄存器的。有點事跨平臺,指令集小,編譯器容易實現(xiàn),
缺點是性能下降,實現(xiàn)同樣的功能需要更多的指令。

4、JVM發(fā)展

虛擬機特點
SUN Classic VM只有解釋器,沒有JIT編譯器
Exact VM編譯器+解釋器 混合工作模式
HotSpot(Oracle)引入了方法區(qū)Method Area。 熱點代碼探測+編譯器解釋器協(xié)同工作
JRockit(Oracle)專注于服務(wù)器端,JRockit不包含解釋器實現(xiàn),全部靠即時編譯器編譯后執(zhí)行
J9(IBM)

二、類加載子系統(tǒng)

  • 類加載器子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載Class文件,class文件在文件開頭有特定的文件標(biāo)識;
  • ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運行,則有執(zhí)行引擎Execution Engine決定;
  • 加載的類信息存放于一塊稱謂方法區(qū)Method Area的內(nèi)存空間。除了類的信息外,方法區(qū)中還會存放運行時常量池信息,可能還包括字符串字面量和數(shù)字常量(這部分常量信息是Class文件中常量池部分的內(nèi)存映射)

在這里插入圖片描述

類加載器子系統(tǒng)分為三個階段:加載階段、鏈接階段、初始化階段

class file加載到 JVM 中,被稱為DNA元數(shù)據(jù)模板,放在方法區(qū)。

.class文件 => JVM => 最終成為元數(shù)據(jù)模板,此過程就要一個運輸工具(類裝載器Class Loader)。

類的加載過程:
在這里插入圖片描述

1、加載Loading

##加載
1、通過一個類的全限定名獲取定義此類的二進制字節(jié)流(從本地、網(wǎng)絡(luò)、ZIP、動態(tài)代理、加密文件中等)2、將這個字節(jié)流鎖代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)3、在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口

類加載器:

JVM支持兩種類型的類加載器:引導(dǎo)類加載器(Bootstrap ClassLoader)自定義類加載器(User-Defined ClassLoader)==>派生于抽象類ClassLoader的類加載器都是自定義類加載器extention classloader和application classloader都是自定義類加載器Bootstrap ClassLoader是C/C++編寫的,其他類加載器是java編寫的他們是包含關(guān)系:其他自定義類加載器< Application classloader < extention classloader < bootstap classloaderJava的核心類庫都是使用bootstrap classloader加載的
1.1、引導(dǎo)類加載器Bootstrap ClassLoader (啟動類加載器)
  • 這個類加載使用C/C++語言實現(xiàn),嵌套在JVM內(nèi)部
  • 它用來加載java的核心類庫,用于提供JVM自身需要的類
  • 加載extention classloader和application classloader,并指定為它們的父類加載器
  • 出于安全考慮,Bootstrap加載器只加載包名為java、javax、sun等開頭的類
  • 它不繼承自java.lang.ClassLoader,沒有父加載器
1.2、擴展類加載器Extension ClassLoader
  • 使用java編寫,由sun.misc.Launcher$ExtClassLoader實現(xiàn)
  • 派生于ClassLoader類
  • 父類加載器為Bootstrap ClassLoader
  • 從java.ext.dir系統(tǒng)屬性所指定的目錄或JDK的安裝目錄的jre/lib/ext子目錄下加載類庫,如果用戶自定義創(chuàng)建的JAR包放在此目錄下,也會由擴展類自動加載
1.3、應(yīng)用程序類加載器Application ClassLoader(系統(tǒng)類加載器)
  • java語言編寫,由sun.misc.Launcher$AppClassLoader實現(xiàn)
  • 派生于ClassLoader類
  • 父類加載器為Extension ClassLoader
  • 負(fù)責(zé)加載環(huán)境變量classpath或系統(tǒng)屬性java.class.path指定路徑下的類
  • 它是程序中默認(rèn)的類加載器,一般java應(yīng)用的類都是由它來完成加載
  • 通過ClassLoader.getSystemClassLoader()方法來獲取該加載器

java的日常應(yīng)用開發(fā)中,類的加載幾乎都是由上述3種類加載器配合執(zhí)行的

演示:


public class ClassLoaderTest {public static void main(String[] args) {//獲取系統(tǒng)類加載器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//獲取擴展類加載器ClassLoader extentionClassLoader = systemClassLoader.getParent();System.out.println(extentionClassLoader);//sun.misc.Launcher$ExtClassLoader@77459877//獲取引導(dǎo)類加載器ClassLoader bootstrapClassLoader = extentionClassLoader.getParent();System.out.println(bootstrapClassLoader);//null//獲取自定義的ClassLoaderTest類的類加載器//說明:對于用戶自定義的類默認(rèn)使用  系統(tǒng)類加載器ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//獲取String的類加載器//Java的核心類庫都是使用bootstrap classloader加載的ClassLoader stringClassLoader = String.class.getClassLoader();System.out.println(stringClassLoader);//null}
}

自定義自己的類加載器,可以繼承ClassLoader或者URLClassLoader類 重寫findClass()方法即可

1.4、雙親委派機制
	JAVA虛擬機對class文件采用的是"按需加載"的方式,也就是說當(dāng)需要使用該類的時候才會將他的class文件加載到內(nèi)存中生成class對象。而且加載某個類的class文件時,java虛擬機采用的是"雙親委派模式",即把請求交由父類處理,也是一種任務(wù)委派模式

在這里插入圖片描述

##工作原理:
1、如果一個類加載器收到了類加載的請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器去執(zhí)行;2、如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸,請求最終將到達頂層的啟動類加載器;3、如果父類加載器可以完成累的加載任務(wù),就成功返回,若父類加載器無法完成此家在任務(wù),子加載器才會
嘗試自己去加載,這就是雙親委派模式##作用:
【只有這樣如果我們自定義的java.lang.String類才不會被執(zhí)行,也就不會覆蓋系統(tǒng)自帶的String了】
1、避免了類的重復(fù)加載
2、保護了程序安全,防止核心API被惡意篡改
1.5、沙箱安全機制
如上,我們自定義java.lang.String類,但是在加載自定義String類的時候會率先使用引導(dǎo)類加載器加載。
從而引導(dǎo)類加載器在加載的過程中會先加載JDK自帶的文件(rt.jar包下的java.lang.String.class)。這樣可以保證對java核心源代碼的保護,這就是"沙箱安全機制"。
1.6、其他
1、在JVM中表示兩個class對象是否為同一個類,有兩個條件:a、類的完整類名必須一致,包括包名b、加載這個類的ClassLoader類加載器必須相同2、JVM必須知道一個類型是由啟動類加載器加載的還是用戶自定義類加載器加載的。
如果一個類型是由用戶自定義類加載器加載的,那么JVM會將這個類加載器的一個引用作為類型信息的一部分保存在方法區(qū)中。
當(dāng)解析一個類型到另一個類型的引用的時候,JVM需要保證這兩個類型的類加載器是相同的。3、JAVA程序?qū)︻惖氖褂梅譃?#xff1a;主動使用 和  被動使用
主動使用,分為7種情況
a、創(chuàng)建類的實例
b、訪問某個類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
c、調(diào)用類的靜態(tài)方法
d、反射(如:Class.forName("com.lee.MyTest"))
e、初始化一個類的自雷
f、Java虛擬機啟動的時候被標(biāo)明為啟動類的類
g、JDK7開始提供的動態(tài)語言支持除了以上情況,其他使用Java類的方式都被看做是隊類的被動使用。主動使用會導(dǎo)致類的Initailization初始化(即下面的初始化),被動使用不會

2、鏈接Liking

##驗證verify文件格式驗證、元數(shù)據(jù)驗證、字節(jié)碼驗證、符號引用驗證(保證類加載的正確性)[.class文件一般以 CA FE BA BE開頭]##準(zhǔn)備Prepare為類"變量"分配內(nèi)存并設(shè)置該類變量的默認(rèn)初始值,即零值。(private static int a = 1;在prepare環(huán)節(jié)a = 0。在initial環(huán)節(jié)才被賦值為1)這里不包含用final修飾的static,因為final在編譯的時候就會分配了,準(zhǔn)備階段會顯示初始化。(private final static int a = 1;在prepare環(huán)節(jié)a = 1)這里不會為實例變量分配初始化,類變量會分配在方法區(qū)中,而實例變量是會隨著對象一起分配到JAVA堆中。##解析Resolve將常量池內(nèi)的符號引用轉(zhuǎn)換為直接引用的過程 

3、初始化Initailization

##初始化
初始化階段就是執(zhí)行類構(gòu)造器方法<clinit>()的過程【<clinit>()不同于類的構(gòu)造器<init>()】此方法不需要定義,是javac編譯器自動收集類中的所有 "類變量的賦值動作(static?)" 和 "靜態(tài)代碼塊中的語句" 合并而來構(gòu)造方法中指令按語句在源文件中出現(xiàn)的順序執(zhí)行若該類具有父類,JVM會保證子類<clinit>()執(zhí)行前父類<clinit>()已經(jīng)執(zhí)行完畢虛擬機必須保證一個類的<clinit>()方法在多線程下被同步加鎖(一個類只能被執(zhí)行一次<clinit>())##解釋
類變量與實例變量的區(qū)別:1)類變量屬于類,可以共享,屬于公共屬性;實例變量屬于某個對象個體;2)加上static 為類變量或者靜態(tài)變量,否則為實例變量;

按順序執(zhí)行

public class ClassInitTest{static{number = 20;//System.out.println(number);//這里可以賦值,但不可以調(diào)用-》非法前向引用}private static int number=10;//在linking的prepare:number=0 --->  initail:按順序執(zhí)行 20 ->10public static void main(String[] args){System.out.println(number);//number的值為10}}

加載子類()執(zhí)行前必須保證父類()的執(zhí)行

public class ClinitTest1{static class Father{public static int A = 1;static{A=2;}}static class Son extends Father{public static int B = A;}public static void main(String[] args){//先加載Father類,prepare:A=0 initial A=1 然后A=2//再加載Son類,prepare:B=0 initail B=A=2System.out.println(Son.B);//2}
}

三、運行時數(shù)據(jù)區(qū)

1、運行時數(shù)據(jù)區(qū)概述及線程

在這里插入圖片描述

Java虛擬機定義了若干種程序運行期間會使用到的運行時數(shù)據(jù)區(qū),其中有些會隨著虛擬機的啟動二創(chuàng)建,隨著虛擬機的退出而銷毀。另外一些則是與線程一一對應(yīng),這些與線程對應(yīng)的數(shù)據(jù)區(qū)域會隨著線程的開始和結(jié)束而創(chuàng)建和銷毀。##上圖:
Method Area方法區(qū) +  Heap Area堆空間 是共享的
Stack Area + PC Registers +  Native Method stack是各個線程獨享的1)、每個線程:獨立使用程序計數(shù)器、棧、本地棧。
2)、線程間共享:堆、堆外內(nèi)存(永久代或元空間、代碼緩存)Method Area方法區(qū) 在 JDK8 之后  成了 元空間,使用的是本地內(nèi)存

關(guān)于線程間共享的說明:

Class Runtime  (java.lang.Runtime)每個JVM只有一個Runtime實例。即為運行時環(huán)境(Run data Area 運行時數(shù)據(jù)區(qū))
[所以Runtime是單例的]

線程:

線程(Thread)是一個程序里的運行單元。JVM允許一個應(yīng)用程序有多個線程并行執(zhí)行。在Hotspot JVM里,每個線程都與操作系統(tǒng)的本地線程直接映射。
(當(dāng)一個Java線程準(zhǔn)備好執(zhí)行以后,此時操作系統(tǒng)的本地線程也同時創(chuàng)建。Java線程執(zhí)行終止后,本地線程也會被回收)操作系統(tǒng)負(fù)責(zé)所有線程的安排調(diào)度到任何一個可用的CPU上,一旦本地線程初始化成功,他就會調(diào)用Java線程的run()方法##Hotspot JVM里主要有如下幾種線程:·虛 擬 機 線 程·周 期 任 務(wù) 線 程·GC 線 程·編 譯 線 程·信 號 調(diào) 度 線 程

2、PC寄存器

沒有GC垃圾回收,不會報OOM(OutOfMemoryError)

##PC寄存器 :Program Counter Register 【類似一個游標(biāo)】
(JVM中的PC寄存器是對屋里PC寄存器的一種抽象模擬,也可以成為程序的鉤子,程序的行號指示器)##作用:
PC寄存器用來存儲指向下一條指令的地址,也就是即將要執(zhí)行的指令代碼。
由執(zhí)行引擎讀取下一條指令##特點:
·它是一塊很小的內(nèi)存空間,也是運行速度最快的存儲區(qū)域·JVM規(guī)范中:每個線程都有它自己的PC寄存器,是線程私有的,生命周期與線程保持一致·任何時間一個線程都只有一個方法在執(zhí)行,也就是所謂的當(dāng)前方法。PC寄存器會存儲當(dāng)前線程
正在執(zhí)行的Java方法的JVM指令地址,或者,如果是在執(zhí)行native方法,則是未指定值[undefined]下面的 棧幀 也就是 方法,當(dāng)前棧幀就是程序中的當(dāng)前Java方法

在這里插入圖片描述

·PC寄存器是程序控制流的指示器,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個計數(shù)器來完成?!ぷ止?jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令?!に俏ㄒ灰粋€在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域
javap -v PCRegister.class下方右側(cè) 0 2 3···即 指令地址 或 偏移地址

在這里插入圖片描述

常見問題:

一、##使用PC寄存器存儲字節(jié)碼指令地址有什么用?##為什么使用PC寄存器記錄當(dāng)前線程的執(zhí)行地址?因為CPU需要不停的快速切換各個線程,切換回來后,就得以知道接著從哪開始繼續(xù)執(zhí)行了。JVM的字節(jié)碼解釋器就需要通過改變PC寄存器的值來明確下一條應(yīng)該執(zhí)行什么樣的字節(jié)碼指令。二、##PC寄存器為什么被設(shè)定為線程私有的?所謂的多線程在特定的時間段內(nèi)只會執(zhí)行其中的某一個線程,CPU會不停地做快速切換而已。
這樣必然導(dǎo)致線程的經(jīng)常中斷或恢復(fù)。為了能夠準(zhǔn)確地記錄各個線程正在執(zhí)行的當(dāng)前字節(jié)碼指令地址,最好的辦法自然是為每個線程都
分配一個PC寄存器,這樣一來各個線程之間變可以進行獨立計算,從而不會出現(xiàn)相互干擾的情況。

拓展:CPU時間片:

CPU時間片即CPU分配給各個程序的時間,每個線程被分配一個時間段,稱作它為時間片。宏觀上:我們可以同時打開多個應(yīng)用程序,每個程序并行不悖,同時運行。
微觀上:由于只有一個CPU,一次只能處理程序要求的一部分,如何處理公平,一種方法就是引入時間片,每個程序輪流執(zhí)行
并行: 多個任務(wù) 被多個CPU同時執(zhí)行,相對的就是  "串行"并發(fā):多個任務(wù) 被一個CPU快速切換的交替執(zhí)行

3、虛擬機棧(Java棧)

3.1、概述

沒有GC垃圾回收問題,但會存在OOM(OutOfMemoryError)的問題

一、##出現(xiàn)背景:前面在JVM架構(gòu)模型中講到,指令流基本上基于兩種指令集架構(gòu):基于棧的指令集架構(gòu)、基于寄存器的指令集架構(gòu)。由于跨平臺性的設(shè)計Java的指令都是根據(jù)棧來設(shè)計的。(不同平臺的CPU架構(gòu)不同,所以不能使用基于寄存器的)。優(yōu)點是:跨平臺,指令集小,編譯器容易實現(xiàn)。
缺點是:性能下降,實現(xiàn)同樣的功能需要更多的指令(指令集小但指令多)二、##內(nèi)存中棧與堆的區(qū)別:棧是運行時的單位,堆是存儲的單位即:棧解決程序的運行問題,即程序如何執(zhí)行或者說如何處理數(shù)據(jù)。
堆解決的是數(shù)據(jù)存儲的問題,即數(shù)據(jù)怎么放,放哪兒。三、##Java虛擬機棧是什么?Java虛擬機棧,早期也叫Java棧。
每個線程在創(chuàng)建時都會創(chuàng)建一個虛擬機棧,其內(nèi)部保存一個個的棧幀(Stack Frame),每一個棧幀對應(yīng)著一個的Java方法調(diào)用。
一個個方法的調(diào)用對應(yīng)著一個個棧幀的入棧和出棧操作。Java棧是線程私有的。其生命周期和線程一致。主管Java程序的運行,它保存 方法的 "局部變量(8種基本數(shù)據(jù)類型、對象的引用地址)"、 "部分結(jié)果",并參與方法的調(diào)用和返回。 四、##棧的有點:棧是一種快速有效的分配存儲方式,訪問速度僅次于程序計數(shù)器。JVM直接對Java棧的操作只有兩個:
·方法的執(zhí)行,伴隨著進棧(入棧、壓棧)
·執(zhí)行結(jié)束后的 出棧 工作對于棧來說不存在GC垃圾回收問題

在這里插入圖片描述

五、##棧中可能出現(xiàn)的異常:Java虛擬機規(guī)范允許Java棧的大小是 "動態(tài)的" 或者 "固定不變"的。 ·如果采用固定大小的Java虛擬機棧,那每一個線程的Java虛擬機棧容量可以在線程創(chuàng)建的時候獨立選定。
如果線程請求分配的棧內(nèi)容超過Java虛擬機允許的最大容量,Java虛擬機將會拋出一個StackOverflowError 異?!具f歸的時候可能會出現(xiàn)】·如果Java虛擬機??梢詣討B(tài)擴展,并且在嘗試擴展的時候無法申請到足夠的內(nèi)存,或者在創(chuàng)建新的線程時沒有足夠的內(nèi)存去創(chuàng)建對應(yīng)的虛擬機棧,那么Java虛擬機將會拋出一個OutOfMemoryError異常六、##設(shè)置棧的大小
【java -Xss256k 設(shè)置stack大小】
【java -Xmx512m -Xmx512m 其中-Xmx設(shè)置堆最大值 -Xms設(shè)置堆初始值?!?
3.2、棧的存儲結(jié)構(gòu)和運行原理
一、##棧的存儲單位每個線程都有自己的棧,棧中的數(shù)據(jù)都是以棧幀Stack Frame的格式存在。在這個線程上正在執(zhí)行的每一個方法都各自對應(yīng)一個棧幀Stack Frame。棧幀是一個內(nèi)存區(qū)塊,是一個數(shù)據(jù)集,維系著方法執(zhí)行過程中的各種數(shù)據(jù)信息。二、##棧運行原理JVM直接對Java棧的操作只有兩個,就是對棧幀的“壓?!焙汀俺鰲!?#xff0c;遵循“先進后出”、“后進先出”的原則。在一條活動線程中,一個時間點上,只會有一個活動的棧幀,即只有當(dāng)前正在執(zhí)行的方法的棧幀
(棧頂棧幀)是有效的,這個棧幀被稱謂當(dāng)前棧幀Current Frame,與當(dāng)前棧幀相對應(yīng)的方法
就是當(dāng)前方法Current Method,定義這個方法的類就是當(dāng)前類Current Class.執(zhí)行引擎運行的所有字節(jié)碼指令只針對當(dāng)前棧幀進行操作。如果在該方法中調(diào)用了其他方法,對應(yīng)的新的棧幀就會被創(chuàng)建出來,放在棧的頂端,稱謂新的當(dāng)前棧幀。不同線程中鎖包含的棧幀是不允許存在相互引用的,即不可能在一個棧幀中引用另外一個線程的棧幀。如果當(dāng)前方法調(diào)用了其他方法,方法返回之際,當(dāng)前棧幀會傳回此方法的執(zhí)行結(jié)果給前一個棧
幀,接著,虛擬機會丟棄當(dāng)前棧幀,使得前一個棧幀重新成為當(dāng)前棧幀。Java方法有兩種返回函數(shù)的方式:一種是正常的函數(shù)返回,使用return指令;另一種是拋異常。
不管使用哪種方式,都會導(dǎo)致棧幀被彈出。
3.3、棧幀的內(nèi)部結(jié)構(gòu)
一、##棧幀的內(nèi)部結(jié)構(gòu)每個棧幀中存儲著:
·局部變量  Local Variables
·操作數(shù)棧  Operand Stack  (或表達式)
·動態(tài)鏈接  Dynamic Linking (或指向運行時常量池的方法引用)
·方法返回地址 Return Address (或方法正常退出 或者異常退出的定義)
·一些附加信息

在這里插入圖片描述

1>、局部變量表Local Variables

·局部變量表也被稱謂 局部變量數(shù)組  或  本地變量表·定義為一個數(shù)字?jǐn)?shù)組,主要用于存儲方法參數(shù)和定義在方法體內(nèi)的局部變量,
這些數(shù)據(jù)類型包括8種基本數(shù)據(jù)類型、對象引用(refrence),以及returnAddress類型·由于局部變量表是建立在線程的棧上,是線程的私有數(shù)據(jù),因此不存在數(shù)據(jù)的安全問題·局部變量表所需的容量大小是在編譯器確定下來的,并保存在方法的Code屬性的maximum local variable是數(shù)據(jù)項中。
在方法運行期間是不會改變局部變量表的大小的。

在這里插入圖片描述

【上圖 LineNumberTable表示 字節(jié)碼的行號 和 java代碼行號對應(yīng)的關(guān)系】·方法嵌套調(diào)用的次數(shù)由棧的大小決定(如遞歸)?!ぞ植孔兞勘碇械淖兞恐辉诋?dāng)前方法調(diào)用中有效。方法執(zhí)行時,虛擬機通過使用局部變量表完成參數(shù)值
到參數(shù)變量列表的傳遞過程。當(dāng)方法調(diào)用結(jié)束后,隨著方法棧幀的銷毀,局部變量表也會隨之銷毀?!揪植孔兞勘硎侵饕绊憲笮〉摹?#上圖中LocalVariableTable中Slot的理解·參數(shù)值的存放總是在局部變量數(shù)組的index0開始,到數(shù)組長度的-1的索引結(jié)束?!ぞ植孔兞勘?#xff0c;最基本的存儲單元是Slot 變量槽·局部變量表中存放編譯期可知的8中基本數(shù)據(jù)類型,引用類型,returnAddress類型的變量·局部變量表里,32位以內(nèi)的類型只占用一個slot(包括returnAddress類型)
64位的類型(long和double)占用兩個slot
> byte\short\char在存儲前被轉(zhuǎn)換為int
> boolean也被轉(zhuǎn)換為int 0表示false,非0表示true
> long和double則占據(jù)兩個slotbyte\short\char\float\int 占1個slot
long\double占2個slot·JVM回味局部變量表中每個slot都分配一個訪問索引,通過這個索引即可成功訪問到局部變量表中指定的局部變量值?!ぎ?dāng)一個實例方法被調(diào)用的時候,他的方法參數(shù)和方法體內(nèi)部定義的局部變量將會按照順序被賦值到局部變量表中的每一個slot上?!と绻枰L問局部變量表中的一個64bit的局部變量值時,只需要使用前一個索引即可。(long和double)·如果當(dāng)前棧幀是由構(gòu)造方法或者實例方法創(chuàng)建的,那么該對象引用this將會存放在index為0的slot處,其余的參數(shù)按照參數(shù)表順序繼續(xù)排列。
(因此被static修飾的方法中是不能夠使用this的)·棧幀中的局部變量表中的槽位slot是可以重復(fù)利用的,如果一個局部變量過了其作用域,那么在其作用域之后
聲明的新的局部變量就很有可能會服用過期局部變量的槽位,從而達到節(jié)省資源的目的。
例如:
public void test{int a = 0;{int b = 0;b = a + 1;}int c = a +1;
}
其中b的作用域在其{}內(nèi),變量c是使用之前已經(jīng)銷毀的變量b占據(jù)的slot的位置。

在這里插入圖片描述

變量 f 的位置被重復(fù)利用了

##拓展變量的分類:按照數(shù)據(jù)類型分:①基本數(shù)據(jù)類型 ②引用數(shù)據(jù)類型按照在類中的聲明位置分:①成員變量:類變量(或叫靜態(tài)變量,被static修飾)實例變量②局部變量	
##類變量linking的prepare階段:給類變量默認(rèn)賦值-->initial階段:給類變量顯式賦值即靜態(tài)代碼塊賦值##實例變量隨著對象的創(chuàng)建,會在堆空間中分配實例變量空間,并進行默認(rèn)賦值
##局部變量在使用前,必須要進行顯式賦值!否則,編譯不通過
##補充說明:·在棧幀中,與性能調(diào)優(yōu)關(guān)系最為模切的部分就是 局部變量表。
在方法執(zhí)行時,虛擬機使用局部變量表完成方法的傳遞。·局部變量表中的變量也是重要的垃圾回收根節(jié)點,只要被局部變量表中引用數(shù)據(jù)類型直接或間接引用的對象都不會被回收。(局部變量表中 存儲:基本數(shù)據(jù)類型 和 引用數(shù)據(jù)類型,引用數(shù)據(jù)類型引用的是堆空間中的對象,所以影響了堆空間的GC垃圾回收。如果局部變量表中的引用數(shù)據(jù)類型不存在了,那么堆空間中的數(shù)據(jù)可能就會被回收)

2>、操作數(shù)棧Operand Stack

##操作數(shù)棧:·后進先出,也被稱之為表達式?!げ僮鲾?shù)棧,主要是用于保存計算過程中的中間結(jié)果,同時作為計算過程中變量臨時的存儲空間·操作數(shù)棧,在方法執(zhí)行的過程中,根據(jù)字節(jié)碼指令,往棧中寫入數(shù)據(jù)或提取數(shù)據(jù),即入棧PUSH/出棧POP
(有些字節(jié)碼指令是將值PUSH進操作棧,有些字節(jié)碼指令是將操作數(shù)POP出棧,使用后再把結(jié)果PUSH進棧)
(比如執(zhí)行賦值、交換、求和等操作)

在這里插入圖片描述

·操作數(shù)棧,主要是用于保存計算過程中的中間結(jié)果,同時作為計算過程中變量臨時的存儲空間。·操作數(shù)棧就是JVM執(zhí)行引擎的一個工作區(qū),當(dāng)一個方法開始執(zhí)行的時候,一個新的棧幀也會隨
之被創(chuàng)建出來,這個方法的操作數(shù)棧是空的?!っ恳粋€操作數(shù)棧都會擁有一個明確的棧深度用于存儲數(shù)值,其所需的最大深度在編譯期就定義
好了。保存在方法的code屬性中,為max_stack的值。·棧中的任何一個元素都是可以任意的Java數(shù)據(jù)類型:>32bit的類型占用一個棧單位深度>64bit的類型占用兩個棧單位深度·操作數(shù)棧并非采用訪問索引的方式來進行數(shù)據(jù)訪問的,而是只能通過標(biāo)準(zhǔn)的入棧和川站操作來完成一次數(shù)據(jù)訪問·如果被調(diào)用的方法帶返回值的話,其返回值將會被壓入當(dāng)前棧幀的操作數(shù)棧中。并更新PC寄存器中下一條需要執(zhí)行的字節(jié)碼指令。
(當(dāng)當(dāng)前方法用到上一條方法的返回值時會從操作數(shù)棧中l(wèi)oad)·操作數(shù)棧中元素的數(shù)據(jù)類型必須與字節(jié)碼指令的序列嚴(yán)格匹配,這由編譯器在編譯器期間進行
驗證,同時在類家在過程中的類檢驗階段的數(shù)據(jù)流分析階段要再次驗證。·另外,我們說Java虛擬機的解釋引擎就是基于棧的執(zhí)行引擎,其中的棧值得就是操作數(shù)棧。
(也就是說 JVM的執(zhí)行引擎和操作數(shù)棧 配合操作的)

棧頂緩存技術(shù)Top-Of-Stack Cashing

·由于操作數(shù)是存儲在內(nèi)存中的,因此頻繁第執(zhí)行內(nèi)存讀/寫操作必然會影響執(zhí)行速度。為了解決這個問題,HotSpot JVM的設(shè)計者提出了棧頂緩存技術(shù)。即將棧頂元素全部緩存在物理CPU的寄存器中,以此降低對內(nèi)存的讀/寫次數(shù),提升執(zhí)行殷勤的執(zhí)行效率。

3>、動態(tài)鏈接Dynamic Linking

動態(tài)鏈接:指向運行時常量池的方法引用。
(運行時常量池  在  "方法區(qū)" 中)·每一個棧幀內(nèi)部都包含一個執(zhí)行運行時常量池中該棧幀所屬方法的引用。
包含這個引用的目的就是為了支持當(dāng)前方法的代碼能夠?qū)崿F(xiàn)動態(tài)鏈接:invokedynamic指令·在Java源文件被編譯到字節(jié)碼文件中時,所有的變量和方法引用都作為符號引用保存在class文件的常量池里。
(比如描述一個方法調(diào)用了另外的其他方法時,就是通過常量池中指向方法的符號引用來表示的)·動態(tài)鏈接的作用就是為了將這些符號引用轉(zhuǎn)換為調(diào)用方法的直接引用

在這里插入圖片描述

·符號引用:以一組符號來描述所引用的目標(biāo) 符號可以是任何形式的字面量,只要使用時能夠無歧義的定位到目標(biāo)即可
如:
public static void main(java.lang.String[]);....Code:stack=2, locals=3, args_size=1....11: invokevirtual #4      // Method methods1:()I·直接引用:直接引用可以是直接指向目標(biāo)的指針、相對偏移量、一個能間接定位到目標(biāo)的句柄
如:
Constant pool:....#4 = Methodref  #2.#39  // com/lee/jvm/rundataarea/LocalVariablesTest.methods1:()I##JVM指令拓展:
普通調(diào)用指令:
invokestatic:調(diào)用靜態(tài)方法
invokespecial:調(diào)用<init>方法、私有及父類方法
invokevirtual:調(diào)用所有虛方法
invokeinterface:調(diào)用接口方法
動態(tài)調(diào)用指令:
invokedynamic:動態(tài)解析出需要調(diào)用的方法,然后執(zhí)行

4>、方法返回地址 Return Address

·方法返回地址:存放調(diào)用該方法的PC寄存器的值·一個方法的結(jié)束,有兩種方式:
>正常執(zhí)行完成
>出現(xiàn)未處理異常,非正常退出·無論哪種方式退出,在方法退出后都返回到該方法被調(diào)用的位置。
方法正常退出是,調(diào)用者的PC計數(shù)器的值作為返回地址,即調(diào)用該方法的指令的下一條指令的地址。
而通過異常退出的,返回地址是要通過異常表來確定,棧幀中一般不會保存這部分信息。
1、執(zhí)行引擎遇到任意一個方法返回的字節(jié)碼指令return,會有返回值傳遞給上層的方法調(diào)用這,簡稱正常完成出口:
>一個方法在正常調(diào)用完成之后究竟需要使用哪一個返回指令還需要根據(jù)方法返回值的實際數(shù)據(jù)類型而定。
>在字節(jié)碼指令中,返回指令包含為ireturn(當(dāng)返回值時boolean\byte\chat\short\int類型時使用)、lreturn(long)、freturn(float)、dreturn(double)以及areturn
另外還有一個return指令提供聲明為void的方法、實力初始化方法、類和接口的初始化方法使用。2、在方法執(zhí)行的過程中遇到異常Exception,并且這個異常沒有在方法內(nèi)處理,也就是只要在本方法的異常表中沒有搜索到匹配的異常處理器,就會導(dǎo)致方法退出。簡稱異常完成出口方法執(zhí)行過程中拋出異常時的異常處理,存儲在一個異常處理表,方便在發(fā)生異常的時候找到處理異常的代碼。##本質(zhì)上,方法的退出就是當(dāng)前棧幀出棧的過程。
此時需要恢復(fù)上層方法的局部變量表、操作數(shù)棧、將返回值亞茹調(diào)用者棧幀的操作數(shù)棧、設(shè)置PC寄存器值等,讓調(diào)用者方法繼續(xù)執(zhí)行下去。正常完成出口和異常完成出口的區(qū)別在于:通過異常完成的出口退出的不會給他的上層調(diào)用者產(chǎn)生任何的返回值。

5>、一些附加信息

棧幀中還允許攜帶與Java虛擬機實現(xiàn)相關(guān)的一些附加信息。如:對程序調(diào)試提供支持的信息

6>、關(guān)于Java棧的思考

一、##舉例棧溢出的情況?如果設(shè)置了棧的大小,??臻g固定,虛擬機棧增加棧幀溢出會出現(xiàn)StackOverflowError
如果沒有設(shè)置了棧的大小,虛擬機棧增加棧幀內(nèi)存溢出或者增加新的線程時會出現(xiàn)OutOfMemeryError二、##調(diào)整棧的大小,就能保證不出現(xiàn)溢出嗎?不能保證,只能保證棧溢出的閾值增大而已。三、##分配的棧內(nèi)存越大越好嗎?不是,內(nèi)存空間固定,某個??臻g變大了,擠占了線程數(shù)或者其他結(jié)構(gòu)的內(nèi)存空間四、##垃圾回收是否會涉及到虛擬機棧?不會,棧的生命周期是隨 線程 的創(chuàng)建和銷毀的,所以不存在GC垃圾回收,如果溢出直接報錯五、##方法中定義的局部變量是否線程安全?不一定,舉例說明
public static void method() {int x = 0;for(int i = 1;i<=10;i++) {x *= i;}System.out.println(x);
}
上面線程安全的,當(dāng)多個線程同時執(zhí)行此方法時,每個線程都會產(chǎn)生一個自己的變量x,每個線程之間互不干擾,不會對其他線程的變量x有影響。public static void method(StringBuilder sb) {sb.append(1);sb.append(2);System.out.println(sb.toString());
}  
上面線程不安全,在這里sb對象是作為參數(shù)傳入的,這意味著它并不是線程私有的,多個線程都可以同時訪問它。所以不是線程安全的。                           public static StringBuilder method() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);return sb;
}
上面線程不安全,雖然sb對象是在方法內(nèi)生成的對象。但是sb作為一個返回變量返回,其他線程可以去拿取它,對它進行并發(fā)的操作。                            public static String method() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);return sb.toString();
}     
上面線程安全,sb消亡了,sb.toString()相當(dāng)于我們又new 了一個string,這個new出來的string可能不安全,但是方法內(nèi)的sb是安全的。

4、本地方法棧

在寫本地方法棧前 先 了解一下本地方法庫本地方法接口

在這里插入圖片描述

##本地方法本地方法就是,一個Native Method就是一個Java調(diào)用一個本地非Java代碼的接口,比如是C或者C++。(即一個Java方法其方法內(nèi)部是由非Java語言實現(xiàn)的)
如:public final native Class<?> getClass();
如:Thread 中 private native void start0();標(biāo)識符native可以與所有其他的java標(biāo)識符連用,但是abstract除外

本地方法棧:

·Java虛擬機棧用于管理Java方法的調(diào)用。而本地房發(fā)展用于管理本地方法的調(diào)用。·本地方法棧,也是線程私有的·允許被是線程固定或者是可動態(tài)擴展的內(nèi)存大小(在內(nèi)存溢出方面和虛擬機棧是相同的)·本地方法時C語言實現(xiàn)的·具體做法是 虛擬機棧 中登記native方法,在 執(zhí)行引擎執(zhí)行時加載本地方法庫。
http://aloenet.com.cn/news/43045.html

相關(guān)文章:

  • 網(wǎng)站備案流程教程seo公司上海牛巨微
  • 網(wǎng)站網(wǎng)頁設(shè)計怎樣百度關(guān)鍵詞指數(shù)
  • 常州網(wǎng)站制作公司多嗎寶雞網(wǎng)站開發(fā)公司
  • 蜜蜂vp加速器七天試用杭州優(yōu)化公司在線留言
  • 環(huán)保業(yè)網(wǎng)站建設(shè)的策劃軟文是指什么
  • 百度網(wǎng)站建設(shè)要多少錢春哥seo博客
  • 容桂銷售型網(wǎng)站建設(shè)知乎關(guān)鍵詞排名
  • 使用wordpress的用戶有哪些電商seo優(yōu)化是什么意思
  • 怎么做淘寶聯(lián)盟網(wǎng)站寧波網(wǎng)站推廣制作
  • 大型網(wǎng)站系統(tǒng)圖seo外包軟件
  • 霸州網(wǎng)站建設(shè)廣州全網(wǎng)推廣
  • 唐山做網(wǎng)站公司網(wǎng)絡(luò)服務(wù)商主要包括
  • 360免費建站為什么注冊不了自己建網(wǎng)站流程
  • 長春網(wǎng)站免費制作百度搜索數(shù)據(jù)查詢
  • 什么網(wǎng)站可以設(shè)計接單做網(wǎng)站推廣seo方法
  • 手機網(wǎng)站建站教育模板下載國外網(wǎng)站seo免費
  • 網(wǎng)站排名標(biāo)準(zhǔn)怎么弄一個自己的網(wǎng)站
  • 不會寫代碼怎樣做網(wǎng)站開發(fā)一個平臺需要多少錢
  • 用凡科做的網(wǎng)站保存不了sem專員
  • 世界局勢最新消息馮耀宗seo博客
  • 360云盤做 網(wǎng)站圖片服務(wù)器濟南網(wǎng)站建設(shè)哪家便宜
  • 中國自適應(yīng)網(wǎng)站建設(shè)朝陽網(wǎng)站seo
  • 醫(yī)藥網(wǎng)站怎么做搜索引擎優(yōu)化是做什么的
  • 國務(wù)院政府網(wǎng)站集約化建設(shè)seo排名優(yōu)化工具
  • 求一個能用的網(wǎng)址網(wǎng)站搜索排名優(yōu)化價格
  • index 石家莊網(wǎng)站建設(shè)友鏈交易平臺源碼
  • 浙江龍游疫情最新消息搜索引擎排名優(yōu)化
  • 編寫 網(wǎng)站 語言海外免費網(wǎng)站推廣有哪些
  • 網(wǎng)站開發(fā)工作描述百度搜索引擎介紹
  • 商用自適應(yīng)網(wǎng)站建設(shè)智能搜索引擎