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

當前位置: 首頁 > news >正文

深圳專門做網(wǎng)站阿里云域名查詢和注冊

深圳專門做網(wǎng)站,阿里云域名查詢和注冊,網(wǎng)站文章做內(nèi)鏈,wordpress手機端m.在上一篇中,介紹了JVM組件中的類加載器,以及相關(guān)的雙親委派機制。這一篇主要介紹運行時的數(shù)據(jù)區(qū)域 JVM架構(gòu)圖: JDK1.8后的內(nèi)存結(jié)構(gòu): (圖片來源:https://github.com/Seazean/JavaNote) 而在運行時數(shù)據(jù)區(qū)域中&#…

在上一篇中,介紹了JVM組件中的類加載器,以及相關(guān)的雙親委派機制。這一篇主要介紹運行時的數(shù)據(jù)區(qū)域

JVM架構(gòu)圖:

JDK1.8后的內(nèi)存結(jié)構(gòu):

?(圖片來源:https://github.com/Seazean/JavaNote) ?

而在運行時數(shù)據(jù)區(qū)域中,根據(jù)線程是否共享可以進行分類:

  • 線程不共享:程序計數(shù)器,本地方法棧,Java虛擬機棧。
  • 線程共享:堆,方法區(qū)。

1、程序計數(shù)器

????????1.1、概述

????????簡稱PC寄存器,用于存儲當前線程正在執(zhí)行的指令的地址或者下一條即將執(zhí)行的指令的地址。在Java虛擬機中,每個線程都有自己獨立的程序計數(shù)器,它是線程私有的,不會被線程切換所影響。

??????? 它記錄了當前線程正在執(zhí)行的字節(jié)碼指令的地址。當線程執(zhí)行一般方法時,程序計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址,當線程執(zhí)行的是本地方法源碼中被native關(guān)鍵字修飾的方法)時,程序計數(shù)器的值為空(Undefined)。

??????? 程序計數(shù)器的作用有以下幾點:

  • 線程切換恢復(fù): 當線程切換回來時,虛擬機通過程序計數(shù)器來確定線程上次執(zhí)行到的位置,從而繼續(xù)執(zhí)行。(例如我現(xiàn)在有A,B兩個線程并發(fā)執(zhí)行某個方法,該方法有10條指令,A線程首先獲得了執(zhí)行權(quán),在執(zhí)行到第4條指令時CPU的時間分片結(jié)束,B線程獲得到了執(zhí)行權(quán),從第1條指令開始執(zhí)行,等待CPU時間分片再次結(jié)束,假設(shè)A線程獲得了執(zhí)行權(quán),就從第4條指令繼續(xù)執(zhí)行。)
  • 指令定位: 程序計數(shù)器指示了當前正在執(zhí)行的虛擬機指令的地址,幫助虛擬機準確定位下一條需要執(zhí)行的指令。
  • 異常處理: 虛擬機使用程序計數(shù)器來記錄異常處理代碼的起始地址,以便異常處理完成后能夠繼續(xù)執(zhí)行原來的代碼。
  • 線程間通信: 在多線程環(huán)境下,程序計數(shù)器也可以用于線程間通信,例如實現(xiàn)輕量級的線程協(xié)作機制。
??????? 1.2、案例

??????? 例如有如下的一段代碼

public class Demo1 {public static void main(String[] args) {int i = 0;if (i ==0){i--;}i++;}
}

??????? 它的字節(jié)碼指令是

?0 iconst_0
?1 istore_1
?2 iload_1
?3 ifne 9 (+6)
?6 iinc 1 by -1
?9 iinc 1 by 1
12 return

??????? 其中每一行開頭處的0,1等代表偏移量,在字節(jié)碼或者內(nèi)存中,偏移量表示了某個數(shù)據(jù)項相對于起始地址的偏移量,以字節(jié)為單位。

??????? 在加載階段,虛擬機將字節(jié)碼的指令讀取到內(nèi)存后,會將偏移量轉(zhuǎn)換為內(nèi)存地址:

??????? 代碼的執(zhí)行過程中,程序計數(shù)器會記錄下一行字節(jié)碼指令的地址,執(zhí)行完當前指令,虛擬機的執(zhí)行引擎會根據(jù)程序計數(shù)器執(zhí)行下一條指令。


2、棧 ??????

??????? 首先明確一個概念:棧區(qū)別于隊列,是一種先進后出的數(shù)據(jù)結(jié)構(gòu),類似于彈夾,先壓入的子彈最后打出,后壓入的子彈最先打出。

??????? 并且在多線程環(huán)境下,棧之間是相互獨立的,這一點在JUC并發(fā)編程篇中做過驗證。

??????? 在JVM中,棧又是由三部分組成:

  • 局部變量表:存放運行時的所有局部變量
  • 操作數(shù)棧:用于存放執(zhí)行過程中的臨時數(shù)據(jù)
  • 幀數(shù)據(jù):包含動態(tài)鏈接,方法出口,異常表引用等
??????? 2.1、局部變量表

??????? 我現(xiàn)在有一段代碼

public class Demo2 {public static void main(String[] args) {int i = 10;long j = 20;}
}

??????? 編譯后通過jclasslib查看:

??????? 表頭的含義:

  • Nr.:代表當前元素的編號,在案例中0代表args,1代表i,2代表j。
  • 起始PC:表示該局部變量的作用域的起始位置,即該局部變量在方法中有效的起始位置:

??????? 這段字節(jié)碼大致的含義是:

  1. 0 bipush 10: 這條字節(jié)碼將整數(shù)10推送到操作數(shù)棧頂。bipush指令用于將一個字節(jié)(-128到127之間的整數(shù))推送到操作數(shù)棧頂。

  2. 2 istore_1: 將操作數(shù)棧頂?shù)恼麛?shù)值(之前推送的10)存儲到索引為1的本地變量中。istore_1指令將整數(shù)值存儲到本地變量表中索引為1的位置。

  3. 3 ldc2_w #2 <20>: 將一個常量(在常量池中的索引為2的項,可能是一個long或double類型的常量)推送到操作數(shù)棧頂。

  4. 6 lstore_2: 將操作數(shù)棧頂?shù)膌ong類型常量值(之前推送的常量)存儲到索引為2的本地變量中。lstore_2指令將long類型的值存儲到本地變量表中索引為2的位置。

  5. 7 return: 從當前方法返回,沒有返回值。return指令用于從當前方法返回,結(jié)束方法的執(zhí)行。

???????? 由此可知,當i變量經(jīng)過了1,2兩步后,才算賦值完成,所以i的作用域是從3開始。j同理。

  • 長度:表示該局部變量的作用域的長度,即該局部變量在方法中有效的長度。
  • 序號:表示該局部變量在局部變量表中的索引位置。局部變量表是按索引順序存儲局部變量的,索引從0開始遞增。

??????? 而在實例方法中(區(qū)別于被static關(guān)鍵字修飾的靜態(tài)方法),序號為0的位置會存放一個this。代表調(diào)用該方法的對象:

public class Demo2 {public static void main(String[] args) {}public void test1(){int i = 10;long j = 20;}
}


??????? 如果是帶有參數(shù)的方法,方法的參數(shù)也是會存放在局部變量表中的,例如main方法的args參數(shù),在第一個案例中就有所體現(xiàn)。

??????? 例如在某個實例方法中,有兩個參數(shù),并且有兩個局部變量,那么在局部變量表中就會有5個元素。


??????? 局部變量表中的序號也是能復(fù)用的:

public class Demo2 {public static void main(String[] args) {}public void test1(int k,int m){{int a = 1;int b = 2;}{int c = 1;}int i = 0;long j = 1;}
}

??????? 上面的案例,在0號索引處存放了this,然后將參數(shù)k,m放在了1,2號索引處,第一個代碼塊中的a,b放在3,4號索引處。

??????? 然后執(zhí)行第二個代碼塊,a,b的作用范圍已經(jīng)結(jié)束了。就會把c放在原先a的3索引的位置。

??????? 最后執(zhí)行給i,j賦值的語句,c的作用范圍也結(jié)束了,就會把i放在原先c的3索引位置,j方法原先b的4索引位置。

??????? 2.2、操作數(shù)棧

??????? 操作數(shù)棧的深度是在編譯期就提前確定的:

??????? 2.3、幀數(shù)據(jù)
??????? 2.3.1、動態(tài)鏈接

??????? 動態(tài)鏈接是指在方法調(diào)用時,JVM需要確定被調(diào)用方法的實際地址或者說是方法在內(nèi)存中的具體位置。由于Java是一種面向?qū)ο蟮恼Z言,方法調(diào)用可能涉及到多態(tài)性,即被調(diào)用方法的具體實現(xiàn)可能在運行時才能確定。

??????? 動態(tài)鏈接會有以下的步驟:

  1. 查找方法: 當一個方法被調(diào)用時,JVM需要查找該方法的具體實現(xiàn)。首先,它會根據(jù)方法調(diào)用指令中的符號引用(Symbolic Reference)去找到對應(yīng)的類和方法,這個過程叫做解析。

  2. 解析: 解析階段會將符號引用解析為直接引用(Direct Reference),即找到被調(diào)用方法在內(nèi)存中的具體位置。這個過程可能會涉及到類加載、鏈接等步驟。

  3. 綁定: 綁定是將方法調(diào)用指令與被調(diào)用方法的具體實現(xiàn)關(guān)聯(lián)起來的過程。動態(tài)綁定是在運行時根據(jù)對象的實際類型來確定方法的具體實現(xiàn)。這種機制允許在程序運行時實現(xiàn)多態(tài)性。

??????? 簡單來說,動態(tài)鏈接表現(xiàn)在編譯期無法確定,只能在運行期間將符號引用轉(zhuǎn)換為直接引用。(編譯和鏈接階段,函數(shù)調(diào)用只是一個符號引用,不包含實際的地址。)

??????? 與之相對的是靜態(tài)鏈接,在編譯階段,所有的函數(shù)調(diào)用在鏈接時就被確定為了直接引用,所有的庫函數(shù)以及其他被調(diào)用的函數(shù)的代碼都會被復(fù)制到可執(zhí)行文件中。(可執(zhí)行文件在運行時不再依賴外部的庫,因為所有的依賴關(guān)系在編譯時已經(jīng)被解決了。)

??????? 一般的場景是,如果沒有依賴外部的庫或動態(tài)鏈接庫,是一個獨立的執(zhí)行文件,則是靜態(tài)鏈接。如果你的程序需要使用系統(tǒng)提供的共享庫或第三方庫,則是動態(tài)鏈接。

??????? 2.3.2、異常表

????????異常表是一種數(shù)據(jù)結(jié)構(gòu),用于管理和處理Java程序中的異常。異常表存儲在方法的字節(jié)碼中,并由JVM在方法執(zhí)行期間使用。

public class Demo1 {public static void main(String[] args) {int i = 0;try {i = 1;} catch (Exception e) {i = 2;}}
}

??????? 對應(yīng)的字節(jié)碼指令:

?0 iconst_0
?1 istore_1
?2 iconst_1
?3 istore_1
?4 goto 10 (+6) ??????? -- 如果沒有發(fā)生異常,就直接跳到第十步。
?7 astore_2
?8 iconst_2
?9 istore_1
10 return

??????? 對應(yīng)的異常表

??????? 其中起始PC和結(jié)束PC就是try...catch塊的作用范圍,跳轉(zhuǎn)PC為出現(xiàn)異常時執(zhí)行的代碼,捕獲類型為捕獲何種異常,在案例中是所有Exception類型的。


??????? 在棧中,是可能存在內(nèi)存溢出問題的,通常的原因是遞歸沒有正確設(shè)置退出條件,導(dǎo)致棧溢出。

public class Demo1 {static int count = 0;public static void main(String[] args) {test1();}private static void test1() {System.out.println(count++);test1();}
}

??????? 在執(zhí)行了大約9800次的時候發(fā)生了棧溢出(StackOverflowError)。

??????? 棧的大小是可以通過JVM參數(shù)進行設(shè)置的,如果沒有設(shè)置棧的大小,JVM也會創(chuàng)建一個默認大小的棧,其大小取決于不同的操作系統(tǒng)。

??????? 如果需要手動修改棧的大小,可以通過JVM參數(shù):-Xss棧大小 實現(xiàn):

??????? 例如我將其設(shè)置成為了512M:

??????? 如果局部變量過多,操作數(shù)棧深度過大也會影響棧內(nèi)存的大小。

3、堆

????????堆內(nèi)存是用于存儲對象實例的內(nèi)存區(qū)域,是 Java 程序中最主要的內(nèi)存區(qū)域之一。堆內(nèi)存由 JVM 在運行時動態(tài)分配和管理,用于存儲所有通過New關(guān)鍵字創(chuàng)建的對象實例以及數(shù)組對象。

??????? 3.1、對象實例

??????? 棧中的局部變量表,可以存放堆上對象的引用:

??????? 同時堆的內(nèi)存也會存在溢出現(xiàn)象(OutOfMemoryError):

public class Demo1 {public static void main(String[] args) throws InterruptedException, IOException {ArrayList<Object> objects = new ArrayList<Object>();while (true){objects.add(new byte[1024 * 1024 * 100]);}}
}


???????? 我們也可以通過arthas工具的dashboard命令進行堆內(nèi)存使用情況的查看:

  • used:代表當前已使用的內(nèi)存。
  • total:代表虛擬機分配的可用堆內(nèi)存。
  • max:是java虛擬機可以使用的最大堆內(nèi)存。

??????? 簡單來說,當used大于等于total時,total會擴容,但是最大不能超過max。

??????? 我們通過在上面的案例的循環(huán)中加上

        while (true){System.in.read();objects.add(new byte[1024 * 1024 * 100]);
//            Thread.sleep(1000);}

??????? 驗證一下,當執(zhí)行了兩次循環(huán)后發(fā)生了擴容:

??????? 堆內(nèi)存的大小也是可以通過JVM命令去設(shè)置的,如果沒有設(shè)置,max默認是系統(tǒng)最大運行內(nèi)存的1/4,total是1/64。

??????? 修改total的命令是:-Xms,修改max的命令是:-Xmx 其中Xms必須大于1M,Xmx必須大于2M,建議將Xms和Xmx設(shè)置成相同的值。

??????? 3.2、字符串常量池

????????字符串常量池用于存儲代碼中定義的常量字符串。在JDK1.8中,字符串常量池不位于方法區(qū)中,而是在堆中(運行時常量池位于直接內(nèi)存的元空間中)。

??????? 例如我現(xiàn)在有以下的代碼:

public class Demo2 {public static void main(String[] args) {String a = "1";String b = "2";String c = "12";String d = a + b;System.out.println(c == d);}
}

??????? 最終運行的結(jié)果是什么?答案是false,通過分析字節(jié)碼指令,其原因在于,當我們執(zhí)行String d = a + b 時,在字節(jié)碼的層面是創(chuàng)建一個StringBuilder的對象,創(chuàng)建的對象會被放在堆內(nèi)存中。

??????? 而c變量的值12是放在字符串常量池中的,所以指向的不是同一個地址(c指向的是字符串常量池中的12,d指向的是堆中的12),使用 == 判斷的結(jié)果是false。

??????? 修改一下上面的案例:

public class Demo3 {public static void main(String[] args) {String a = "1";String b = "2";String c = "12";String d = "1" + "2";System.out.println(c == d);}
}

??????? 運行結(jié)果是true,執(zhí)行?String d = "1" + "2";時不會產(chǎn)生新的對象,而是從字符串常量池中找到c變量的12。

??????? 3.3、靜態(tài)變量

??????? 在JDK1.8后,靜態(tài)變量存放在堆中,靜態(tài)變量是屬于類的,而不是屬于類的實例,因此它們只會在類被加載時被初始化,并且在整個應(yīng)用程序的生命周期內(nèi)存在,直到應(yīng)用程序結(jié)束或者類被卸載。

4、本地內(nèi)存

????????4.1、方法區(qū)

????????用于存儲類信息、常量、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)。

??????? 主要包含了:

  1. 類信息存儲: 方法區(qū)主要用于存儲加載的類信息,包括類的結(jié)構(gòu)信息、字段信息、方法信息、父類信息、接口信息等。每個加載的類都有對應(yīng)的 Class 對象在方法區(qū)中存儲。

  2. 常量池: 方法區(qū)包含了常量池(Constant Pool),用于存儲類中的常量信息,如字符串常量、基本類型常量、符號引用等。常量池在類加載時被創(chuàng)建,包括編譯時生成的常量和運行時生成的常量。

  3. 靜態(tài)變量: 方法區(qū)還存儲了類的靜態(tài)變量,即被static修飾的類級別的變量。這些變量在類加載時被初始化,并在整個應(yīng)用程序的生命周期內(nèi)保持不變。

  4. 即時編譯器產(chǎn)生的代碼: 方法區(qū)還用于存儲即時編譯器(Just-In-Time Compiler,JIT)編譯后的本地機器代碼,這些代碼用于提高 Java 程序的執(zhí)行效率。

  5. 運行時常量池: 除了類加載時的常量池,方法區(qū)還包含了運行時常量池,它是在類加載完成后在方法區(qū)中動態(tài)生成的,用于存儲運行時解析的常量信息。

??????? 在JDK1.8之后,方法區(qū)中的永久代(Permanent Generation)元數(shù)據(jù)區(qū)(Metaspace)所取代。

??????? 復(fù)習(xí)一下,在類的生命周期的加載階段,類加載器加載完成后,JVM會將讀取到的字節(jié)碼信息保存到內(nèi)存的方法區(qū)中,生成一個InstanceKlass對象,保存類的基本信息。

??????? 方法中的靜態(tài)常量池,連接階段后,會將符號引用改變成直接引用。(連接階段中的解析階段,會將常量池中的符號引用替換成直接引用)。

??????? 上面提到過棧和堆都有可能存在內(nèi)存溢出的問題,而方法區(qū)同樣可能會內(nèi)存溢出:

  • 在JDK1.7及以前的版本中,方法區(qū)位于堆中的永久代空間。
  • 在JDK1.8及以后的版本中,方法區(qū)位于元空間中,和堆一樣是獨立的空間。(本地內(nèi)存

??????? 這樣就造成了,在JDK1.7以前的版本,方法區(qū)大小受限于堆的大小,而之后的版本,方法區(qū)的大小則取決于操作系統(tǒng)的直接內(nèi)存大小。

???????? 同樣也可以使用-XX:MaxMetaspaceSize= 命令分配元空間的大小。

????????4.2、直接內(nèi)存

????????是一種在 Java 中進行內(nèi)存分配和管理的機制,它不同于傳統(tǒng)的 Java 堆內(nèi)存和棧內(nèi)存。直接內(nèi)存并不是由 JVM 直接管理的,而是由操作系統(tǒng)管理的一塊內(nèi)存區(qū)域。

??????? 主要用于提高IO的效率,優(yōu)勢在于它可以通過操作系統(tǒng)的零拷貝技術(shù)來實現(xiàn)高效的數(shù)據(jù)傳輸。在進行 I/O 操作或者進行大規(guī)模數(shù)據(jù)處理時,直接內(nèi)存能夠直接與操作系統(tǒng)進行交互,避免了數(shù)據(jù)的多次復(fù)制和拷貝,從而提高了系統(tǒng)的性能和效率。

??????? NIO在讀寫文件時,會將其放入直接內(nèi)存,并且在上維護對直接內(nèi)存地址的引用。

??????? 如果需要創(chuàng)建直接內(nèi)存,可以使用:

ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);

??????? 而直接內(nèi)存和堆,棧,方法區(qū)一樣,同樣會存在內(nèi)存溢出的問題:

??????? 如果需要手動調(diào)整直接內(nèi)存大小,可以通過JVM命令-XX:MaxDirectMemorySize = 大小


補充:

運行時常量池和常量池表:

  • 運行時常量池是每個類或接口的一部分,用于存儲編譯時生成的字面量常量和符號引用。除了字符串常量外,運行時常量池還包含其他類型的常量,如整數(shù)常量、浮點數(shù)常量等。運行時常量池是類加載過程中的一部分,在類加載后會被存儲在方法區(qū)(JDK 8 及之前)或元空間(JDK 8 及之后)中。
  • 常量池表是 class 文件中的一部分,用于存儲編譯時生成的常量信息。它包含了類或接口中的所有常量,包括字符串常量、符號引用、方法名、字段名等。常量池表中的每個常量都有一個索引,可以通過索引來訪問常量池中的具體內(nèi)容。運行時常量池實際上是常量池表在運行時被加載到內(nèi)存中的形式之一。

??????? 常量池表在類加載后成為運行時常量池。

http://m.aloenet.com.cn/news/42121.html

相關(guān)文章:

  • 簡易網(wǎng)站開發(fā)巨量引擎
  • 電子商務(wù)網(wǎng)站服務(wù)器上海網(wǎng)絡(luò)營銷公司
  • dw手機網(wǎng)站建設(shè)500個游戲推廣群
  • 班級網(wǎng)站素材下載百度推廣有哪些推廣方式
  • 大型網(wǎng)站制作梅州網(wǎng)絡(luò)推廣
  • 百度網(wǎng)站錄入網(wǎng)站整站優(yōu)化
  • 建行app官方下載搜索引擎優(yōu)化中的步驟包括
  • 淘寶優(yōu)惠券微網(wǎng)站開發(fā)手機版怎么用百度快照
  • wordpress固定鏈接是存在哪個表襄陽seo優(yōu)化排名
  • 如何查看一個網(wǎng)站是用什么程序做的南寧企業(yè)官網(wǎng)seo
  • 廣東網(wǎng)站建設(shè)公司報價表濟南做網(wǎng)站公司哪家好
  • 中企動力合作網(wǎng)站銷售找客戶的方法
  • 凡科建站免費版可以做什么2021熱門網(wǎng)絡(luò)營銷案例
  • 網(wǎng)站建設(shè)需要了解哪些方面seo上海培訓(xùn)
  • 校園云網(wǎng)站建設(shè)如何快速推廣網(wǎng)站
  • 遼寧建設(shè)廳規(guī)劃設(shè)計網(wǎng)站如何在百度做免費推廣產(chǎn)品
  • 如何做旅游網(wǎng)站seo軟件定制
  • 西安網(wǎng)站建設(shè)制作價格百度客戶端登錄
  • 一個網(wǎng)站如何做推廣百度搜索風(fēng)云榜總榜
  • 男女做爰真人視頻免費網(wǎng)站b2b網(wǎng)站有哪些平臺
  • 如何做淘寶客自己的網(wǎng)站營銷網(wǎng)站方案設(shè)計
  • 大連有幾家做網(wǎng)站的公司天津seo優(yōu)化排名
  • 企業(yè)公司網(wǎng)站 北京企業(yè)文化理念
  • 承德網(wǎng)站制作多少錢高質(zhì)量軟文
  • 做威客哪個網(wǎng)站好石家莊新聞
  • 網(wǎng)站開發(fā)涉及內(nèi)容做推廣的技巧
  • 做濾芯的網(wǎng)站seo軟件工具
  • 網(wǎng)站建設(shè)陜icp百度如何快速收錄網(wǎng)站
  • wordpress googleapisseo軟件
  • 六安哪家公司做網(wǎng)站好搜索引擎優(yōu)化面對哪些困境