国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

這篇具有很好參考價值的文章主要介紹了Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

一:JVM引言

1. 什么是 JVM ?

2. 常見的 JVM

3. 學(xué)習(xí)路線

二:JVM內(nèi)存結(jié)構(gòu)

1. 程 序 計 數(shù) 器(PC Register)

2. 虛 擬 機 棧(JVM Stacks)

3. 本 地 方 法 棧(Native Method Stacks)

4. 堆(Heap)

5. 方 法 區(qū)(Method Area)

三:直接內(nèi)存


tips:首先給大家推薦兩款好用的免費軟件:動圖抓取軟件:ScreenToGif和錄屏工具:oCam,可用來作為日常的制作動圖Gif和錄屏,網(wǎng)盤鏈接:夸克網(wǎng)盤分享

一:JVM引言

1. 什么是 JVM ?

定義:Java Virtual Machine - java 程序的運行環(huán)境(java 二進制字節(jié)碼的運行環(huán)境)

好處:

①一次編寫,到處運行;?

②自動內(nèi)存管理,具有垃圾回收功能;

③數(shù)組下標(biāo)越界檢查,拋出異常;

④多態(tài),面向?qū)ο蟮幕?/p>

比較:JVM、 JRE、 JDK

從圖中我們也可以看出是逐級向上的、包含的關(guān)系!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

2. 常見的 JVM

最常用的就是:HotSpot,Oracle JDK edition、Eclipse OPenJ9;接下來的講解都是基于HotSpot!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

3. 學(xué)習(xí)路線

主要分為三大塊:類加載器ClassLoader、JVM內(nèi)存結(jié)構(gòu)、執(zhí)行引擎。

學(xué)習(xí)順序:先學(xué)習(xí)JVM內(nèi)存結(jié)構(gòu)、然后學(xué)習(xí)GC垃圾回收機制、再學(xué)習(xí)JavaClass字節(jié)碼、然后學(xué)習(xí)類加載器ClassLoader、最后學(xué)習(xí)執(zhí)行引擎的其它內(nèi)容。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

二:JVM內(nèi)存結(jié)構(gòu)

1. 程 序 計 數(shù) 器(PC Register)

(1)定義

ProgramCounterRegister 程序計數(shù)器 ( 寄 存 器 )

特 點: 是線程私有的、 不會存在內(nèi)存溢 出!

(2)作用

執(zhí)行過程:Java源代碼---》經(jīng)過編譯生成二進制字節(jié)碼(一些JVM指令)---》經(jīng)過解釋器---》解釋成機器碼---》最后交給CPU來執(zhí)行!

程序計數(shù)器的作用:在程序執(zhí)行的過程中記住下一條JVM指令的執(zhí)行地址(前面的數(shù)字就可以理解為執(zhí)行地址)。例如:拿到第一條getstatic指令交給解釋器、解釋器變成機器碼、機器碼交給CPU;于此同時會把下一條指令(astore_1)的地址(3),放入程序計數(shù)器,等到第一條指令執(zhí)行結(jié)束,解釋器就會程序計數(shù)器中取下一條指令(astore_1)的地址(3),依次重復(fù)!

思考:如果沒有程序計數(shù)器會有什么問題?

就會造成接下來不知道執(zhí)行哪一條指令!實際上程序計數(shù)器在物理上是通過寄存器實現(xiàn)的!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

2. 虛 擬 機 棧(JVM Stacks)

棧:是一種數(shù)據(jù)結(jié)構(gòu),先進后出或者說后進先出;一個線程一個棧!

(1)定義

Java Virtual Machine Stacks (Java 虛擬機棧)

①每個線程運行時所需要的內(nèi)存,稱為虛擬機棧;

②每個棧由多個棧幀(Frame)組成,對應(yīng)著每次方法調(diào)用時所占用的內(nèi)存;

③每個線程只能有一個活動棧幀,對應(yīng)著當(dāng)前正在執(zhí)行的那個方法。

總結(jié):

---》對應(yīng)著線程運行所需要的內(nèi)存空間。

棧幀---》對應(yīng)著每個方法運行時所需要的內(nèi)存空間。

我們可以通過下面一段代碼理解棧和棧幀,通過Debug模式

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

問題分析:

(1)垃圾回收是否涉及棧內(nèi)存?

答:不涉及,棧幀內(nèi)存每次方法結(jié)束后,都會彈出棧,自動釋放回收;我們知道垃圾回收機制只能回收堆內(nèi)存的無用對象。

(2)棧內(nèi)存分配越大越好嗎?

答:不是,棧內(nèi)存越大,會導(dǎo)致線程數(shù)變少,因為物理內(nèi)存的大小是一定的。例如:一個線程分配1M,物理內(nèi)存總共500M,理論上只能分配500個線程;若一個線程分配2M,理論上只能分配250個線程!

注:棧內(nèi)存劃分大了,只是能夠進行更多次的方法調(diào)用,并不會使運行效率提高!

注:在運行時,可以使用 -Xss size來指定分配的棧內(nèi)存大小;默認情況下:Linux、macOS分配的是1024KB,對于Windows是根據(jù)虛擬內(nèi)存大小進行分配

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(3)方法內(nèi)的局部變量是否線程安全?

例1:分析多個線程調(diào)用是否會使變量x的值混亂?

我們知道一個線程一個棧,對于不同的線程進行調(diào)用,都會產(chǎn)生新的棧幀,每個線程都有自己私有的變量x。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

例2:分析多個線程調(diào)用方法,能否保證線程安全?

①m1方法,局部變量,沒有逃離方法的作用范圍,方法結(jié)束變量釋放,線程安全的;

②m2方法,方法中的變量(前提是引用數(shù)據(jù)類型),其它線程可以通過這個方法進行調(diào)用,非線程安全的;

③m3方法,把這個局部變量(前提是引用數(shù)據(jù)類型)通過return返回,其它線程可以接收,然后執(zhí)行其它操作;

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

總結(jié):

①如果方法內(nèi)局部變量沒有逃離方法的作用范圍,它是線程安全的;

②如果是局部變量是一個引用類型(基本數(shù)據(jù)類型肯定還是線程安全的),并逃離方法的作用范圍,需要考慮線程安全;

(2)棧內(nèi)存溢出

情況一:棧幀過多導(dǎo)致棧內(nèi)存溢出

棧的大小是固定的,假如我們不斷的調(diào)用,使得棧幀不斷的壓棧,最終就會導(dǎo)致棧內(nèi)存溢出;例如:遞歸調(diào)用,沒有結(jié)束條件,最終拋出StackOverflowError異常!

情況二:棧幀過大導(dǎo)致棧內(nèi)存溢出

棧幀過大,一下子就超過了棧的大小;很少見!

(3)線程運行診斷

案例1: cpu 占用過多(有可能是死循環(huán))

定位 :在Linux環(huán)境下,運行Java代碼,nohub java 類 &

nohub:意思是不掛斷 。使用Xshell 等Linux 客戶端工具,遠程執(zhí)行 Linux 腳本時,有時候會由于網(wǎng)絡(luò)問題,導(dǎo)致客戶端失去連接,終端斷開,腳本運行一半就意外結(jié)束了。這種時候,就可以用nohup 指令來運行指令,即使客戶端與服務(wù)端斷開,服務(wù)端的腳本仍可繼續(xù)運行。

&:表示在后臺進行運行。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

先使用top命令可以定位哪個進程對cpu的占用過高

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

ps H?-eo pid,tid,%cpu | grep 進程id用ps命令進一步定位是哪個線程引起的cpu占用過高)

H:顯示樹狀結(jié)構(gòu),表示進程間的相互關(guān)系

-eo:規(guī)定輸出那些感興趣的內(nèi)容,例如:進程id(pid)、線程id(tid)、Cpu的占用情況(%cpu)

|:代表管道符,經(jīng)常與grep篩選命令一起使用

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

jstack 進程id:可以根據(jù)線程id 找到有問題的線程,進一步定位到問題代碼的源碼行號!

注意:上面顯示的32665線程號是十進制,而jstack顯示的十六進制對應(yīng)的就是7F99

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

案例2:程序運行很長時間沒有結(jié)果 (有可能是發(fā)生了線程的死鎖)

先執(zhí)行Java程序,nohub java 類 &,就會顯示進程id

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?jstack 進程id:此時我們無法獲知線程id,看末尾執(zhí)行結(jié)果的提示

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

何時發(fā)生線程死鎖?

對于一個類,含有a、b屬性,對于t1線程先鎖a,在鎖b;對于t2線程先鎖b,在鎖a;這種情況程序就會僵持在哪里,也沒有拋出異常,這種情況下的排查錯誤是非常難的!

3. 本 地 方 法 棧(Native Method Stacks)

定義:JVM調(diào)用本地方法時,需要給這些本地方法提供的內(nèi)存空間!

解釋本地方法(Native Method):指那些不是由Java代碼編寫的方法,例如:利用C、C++編寫的本地方法來與操作系統(tǒng)打交道,Java代碼可以通過這些本地方法來調(diào)用到這些底層的功能;只寫本地方法使用的內(nèi)存就是本地方法棧!

例如:Object類的克隆方法

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

4. 堆(Heap)

前面學(xué)習(xí)的棧Stack是線程私有的、堆Heap是線程共享的!

堆(Heap):通過 new 關(guān)鍵字,創(chuàng)建對象都會使用堆內(nèi)存

特點:

①它是線程共享的,堆中對象都需要考慮線程安全的問題 ;

②有垃圾回收機制;

(1)堆 內(nèi) 存 溢 出

首先先看下面這段代碼:

首先創(chuàng)建一個ArrayList集合,寫一個死循環(huán),字符串不斷進行拼接,然后放到List集合當(dāng)中!

public class Demo1_5 {

    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "hello";
            while (true) {
                list.add(a); // hello, hellohello, hellohellohellohello ...
                a = a + a;  // hellohellohellohello
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println(i);
        }
    }
}

執(zhí)行結(jié)果:內(nèi)存溢出,拋出OutOfMemoryError異常

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

可以使用 -Xmx size來指定分配的堆空間大小

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(2)堆 內(nèi) 存 診 斷

①jps工具:查看當(dāng)前系統(tǒng)中有哪些java進程

②jmap工具:查看堆內(nèi)存占用情況,查看的是某一個時刻;?jmap -heap 進程id

③jconsole工具:圖形界面的,多功能的監(jiān)測工具,可以連續(xù)監(jiān)測

案例1:

首先先創(chuàng)建一個byte數(shù)組,會在堆內(nèi)存中開辟10M的空間;然后把數(shù)組的引用arr置為null,開啟垃圾回收機制進行回收;中間的sleep睡眠是為了方便執(zhí)行指令進行監(jiān)控。

public class Demo1_4 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("1...");
        Thread.sleep(30000);
        byte[] array = new byte[1024 * 1024 * 10]; // 10 Mb
        System.out.println("2...");
        Thread.sleep(20000);
        array = null;
        System.gc();
        System.out.println("3...");
        Thread.sleep(1000000L);
    }
}

使用IDEA運行此程序,打開自帶的dos窗口,輸入命令

①先輸入jps命令,查看有哪些Java進程

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②使用jmap進行檢測

第一步:在控制臺打印輸出1,也就是未創(chuàng)建10M內(nèi)存空間時,使用jmap -heap 18756進行檢測

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

第二步:在控制臺上打印出2,使用jmap -heap 18756進行檢測(此時創(chuàng)建10M的空間)

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

第三步:?在控制臺上打印出3,使用jmap -heap 18756再次進行檢測(此時引用置為null),并啟用了垃圾回收機制進行回收

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?③使用jconsole進行檢測(圖形化界面進行顯示)

步驟:直接輸入jconsole--->顯示圖形化界面,找到要檢測的類---》選擇不安全連接;就會動態(tài)顯示每一個時刻的檢測效果!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

案例2:調(diào)用垃圾回收后,內(nèi)存占用仍然很高

這里先把這段代碼給出來,假如我們不知道代碼的具體實現(xiàn),怎么去一步排查

import java.util.ArrayList;
import java.util.List;

public class ClazzMapperTest {

    public static void main(String[] args) throws InterruptedException {
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 200; i++) {
            students.add(new Student());
        }
        Thread.sleep(1000000000L);
    }
}
class Student {
    private byte[] big = new byte[1024*1024];
}

第一步:使用jps查看進程的id

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

第二步:使用jmap -head 進程id查看內(nèi)存使用情況;分為兩部分:

Eden區(qū):?

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

Old Generation區(qū):?

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

第三步:使用jconsole工具,執(zhí)行垃圾回收機制GC;發(fā)現(xiàn)相對于最初的狀態(tài)確實回收了一部分內(nèi)存,但是還有200多M沒有被回收!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

第四步:實際上200多M,Eden區(qū)確實被回收了不少,但是Old Generation區(qū)卻沒有被回收;使用更加好用的檢測工具jvisualvm(JDK9以后就沒有了,需要下載插件)進行檢測

①找到堆 Dump表示抓取當(dāng)前堆的快照

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②?查找前20個占用堆內(nèi)存最大的對象

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

③可以找到占用堆內(nèi)存最大的對象是一個ArrayList對象

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

④點擊去ArrayList,查看它的屬性都是Student對象?;總共有244個項,其中Student項有200個,其它的都是Object對象(已經(jīng)被釋放掉了);一個Student對象占用1M左右,200個就是占用200多兆,這樣就能排查出來。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

⑤再結(jié)合源碼分析,在主方法main執(zhí)行結(jié)束之前(調(diào)用了sleep方法睡眠),ArrrayList集合中存儲了大量的Student對象,無法釋放;最終使得垃圾回收后,內(nèi)存占用仍然很高!

5. 方 法 區(qū)(Method Area)

(1)定義

(1)方法區(qū)域類似于用于傳統(tǒng)語言的編譯代碼的存儲區(qū)域,或者類似于操作系統(tǒng)進程中的“文本”段。它存儲每個類的結(jié)構(gòu),例如運行時常量池、字段和方法數(shù)據(jù),以及方法和構(gòu)造函數(shù)的代碼,包括類和實例初始化以及接口初始化中使用的特殊方法。方法區(qū)域是在虛擬機啟動時創(chuàng)建的方法區(qū)域在邏輯上是堆的一部分、方法區(qū)域中的內(nèi)存無法滿足分配請求,Java虛擬機將拋出OutOfMemoryError。

(2)特點:

①方法區(qū)是線程共享的,如果多個線程用到同一個類的時候,若這個類還未被加載,此時只能有一個線程去加載類,其他線程需要等待;

②方法區(qū)的大小可以是非固定的,jvm可以根據(jù)應(yīng)用需要動態(tài)調(diào)整,jvm也支持用戶和程序指定方法區(qū)的初始大??;

③方法區(qū)有垃圾回收機制,一些類不再被使用則變?yōu)槔?,需要進行垃圾清理。

(2)組成

JVM1.6版本內(nèi)存結(jié)構(gòu):

使用一個PermGen永久代作為方法區(qū)的實現(xiàn),這個永久代包括以下信息:Class類的信息、ClassLoader類加載器信息、StringTable(字符串表)運行時常量池;

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

JVM1.8版本內(nèi)存結(jié)構(gòu):

使用一個Metaspace元空間作為方法區(qū)的實現(xiàn),存儲以下信息:Class類的信息、ClassLoader類加載器信息、常量池(和上面不同的地方);已經(jīng)不占用堆內(nèi)存了,換句話說不是由JVM來管理它的內(nèi)存結(jié)構(gòu)了;移到本地內(nèi)存(操作系統(tǒng)內(nèi)存)

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?(3)方法區(qū)內(nèi)存溢出

①JDK1.8以前會導(dǎo)致永久代內(nèi)存溢出

我們沒有設(shè)置內(nèi)存的上限,它會把10000個類全都加載到內(nèi)存當(dāng)中,可以使用參數(shù)進行設(shè)置,指定源空間內(nèi)存的大?。?span style="color:#0d0016;">-XX:MaxPermSize=8m

package cn.itcast.jvm.t1.metaspace;

import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;

// 把下面10000個類加載到內(nèi)存當(dāng)中
public class Demo1_8 extends ClassLoader { // 可以用來加載類的二進制字節(jié)碼
    public static void main(String[] args) {
        int j = 0;
        try {
            Demo1_8 test = new Demo1_8();
            for (int i = 0; i < 20000; i++, j++) {
                // ClassWriter 作用是生成類的二進制字節(jié)碼
                ClassWriter cw = new ClassWriter(0);
                // 版本號, public, 類名, 包名, 父類, 接口
                cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                // 返回 byte[]
                byte[] code = cw.toByteArray();
                // 執(zhí)行了類的加載
                test.defineClass("Class" + i, code, 0, code.length); // Class 對象
            }
        } finally {
            System.out.println(j);
        }
    }
}

加上-XX:MaxPermSize=8m 只循環(huán)了19314次,就拋出了永久代溢出異常

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②JDK1.8之后會導(dǎo)致元空間內(nèi)存溢出

相同的代碼,使用參數(shù)進行設(shè)置,指定源空間內(nèi)存的大?。?span style="color:#0d0016;">-XX:MaxMetaspaceSize=8m

加上-XX:MaxMetaspaceSize=8m 只循環(huán)了5411次,就拋出了元空間內(nèi)存溢出

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(4)運行時常量池

①先理解常量池

對于二進制字節(jié)碼,包括類基本信息,常量池,類方法定義,包含了虛擬機指令;先看以下代碼,編譯生成HelloWorld.class文件,使用:javap -v HelloWorld.class進行反編譯

package cn.itcast.jvm.t5;
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

常量池,就是一張表,虛擬機指令根據(jù)這張常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量等信息,例如:??

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②運行時常量池

常量池是 *.class 文件中的;當(dāng)該類被加載,它的常量池信息就會放入運行時常量池,并把里面的符號地址變?yōu)檎鎸嵉刂?。

(5)StringTable

StringTable特性:

①常量池中的字符串僅是符號,第一次用到時才變?yōu)閷ο?/span>

② 利用串池的機制,來避免重復(fù)創(chuàng)建字符串對象

③ 字符串變量拼接的原理是 StringBuilder (1.8);

④ 字符串常量拼接的原理是編譯期優(yōu)化 ;

⑤可以使用 intern方法,主動將串池中還沒有的字符串對象放入串池;

對于1.8 :將這個字符串對象嘗試放入串池,如果有則并不會放入;如果沒有則放入串池, 會把串池中的對象返回。

對于1.6: 將這個字符串對象嘗試放入串池,如果有則并不會放入;如果沒有會把此對象復(fù)制一份, 放入串池, 會把串池中的對象返回。

驗證上面的特性:

String s1 = "a";
String s2 = "b";
String s3 = "ab";

進行反編譯:

#2就對應(yīng)著String a,#3就對應(yīng)著String b,#4就對應(yīng)著String ab

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

、

?astore_1就把加載好的字符串對象存入1號局部變量s1,其它依次類推

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

常量池是存在字節(jié)碼文件.class里,當(dāng)運行的時候會放到運行時常量池當(dāng)中;但是加載到運行時常量池當(dāng)中時,還沒有成為java字符串對象,直到具體執(zhí)行到引用它的那一行代碼;例如:執(zhí)行到String s1 = "a",會把ldc #2 會把a符號變?yōu)椤癮”字符串對象;此時還會準(zhǔn)備一塊空間StringTable,把“a”字符串對象放進去(如果里面沒有的話),這實際上是一個延遲加載(懶惰的)行為;如果串池中有的話,就會直接使用,總而言之,只會存在一份!

所以:會把s1、s2、s3引用指向的“a”、“b”、“ab”放到字符串常量池StringTable當(dāng)中,StringTable [ "a", "b" ,"ab" ] 底層是hashtable結(jié)構(gòu),不能擴容。

String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;

s4=s1+s2,變量拼接,s4引用首先會會創(chuàng)建一個StringBuilder對象,然后調(diào)用append方法,把“a”和“b”拼接進去,然后調(diào)用toString方法;我們通過查看StringBuilder的toString方法底層原碼發(fā)現(xiàn)是創(chuàng)建一個新的字符串對象:new String("ab")。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
// 問
System.out.println(s3 == s4);

s3 == s4的結(jié)果?

s3對應(yīng)的"ab"是在字符串常量池中的對象,但s4是一個新創(chuàng)建的字符串對象,雖然值是相同的,但是s3是在串池當(dāng)中的,s4是先創(chuàng)建出來的,== 對比的就是地址,肯定是不一樣的,結(jié)果是false。

String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2; // 變量拼接
String s5 = "a" + "b"; // 常量拼接
// 問
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true

s5 = "a" + "b" 直接找的就是已經(jīng)拼接好的“ab”對象;這是javac在編譯器的優(yōu)化,在編譯期間我們就能確定肯定就是“ab”對象,此時常量池中已經(jīng)存在這個對象,所以 s3 == s5的結(jié)果就是true。

注:s4=s1+s2是在運行期間才能確定,去動態(tài)拼接!

JDK1.8:會將這個字符串對象嘗試放入串池,如果有則并不會放入,如果沒有則放入串池, 會把串池中的對象返回!

String s = new String("a") + new String("b");
// 使用JDK1.8,會將這個字符串對象嘗試放入串池,
// 如果有則不會放入,如果沒有則放入串池,并把串池中的對象返回
String s2 = s.intern();
System.out.println(s2 == "ab"); // true
System.out.println(s == "ab"); // true

s = new String("a") + new String("b"); 首先會把“a”和“b”放到常量池當(dāng)中,但是s="ab"不會放進去,因為是變量拼接,會創(chuàng)建一個字符串對象,是存放在堆當(dāng)中,要想把"ab"放到常量池當(dāng)中,可以調(diào)用intern方法,這樣就可以把"ab"放入字符串常量池當(dāng)中。此時就把s的對象放入常量池當(dāng)中,并且s2是串池中對象的返回值;所以兩個都為true。

String s = new String("a") + new String("b");

String x = "ab";

String s2 = s.intern();
System.out.println(s2 == x); // true
System.out.println(s == x); // false

此時String x = "ab',會把“ab”放到串池當(dāng)中;此時串池當(dāng)中已經(jīng)存在“ab”對象,此時s.intern(),就不會把“ab”對象放入串池當(dāng)中;而s2 = s.intern返回的一定是一個串池當(dāng)中的對象;所以此時s2 == x是true,s == x是false。

JDK1.6:將這個字符串對象嘗試放入串池,如果有則并不會放入,如果沒有會把此對象復(fù)制一份, 放入串池, 會把串池中的對象返回!

String s = new String("a") + new String("b");
// 使用JDK1.6,會將這個字符串對象嘗試放入串池,
// 如果有則并不會放入,如果沒有會把此對象復(fù)制一份,放入串池, 會把串池中的對象返回
String s2 = s.intern();
System.out.println(s2 == "ab"); // true
System.out.println(s == "ab"); // false

使用JDK1.6最主要的區(qū)別就是s.intern,此時是拷貝一份放入串池當(dāng)中,而不是把s本身的對象放入串池,s還是堆中的對象,此時s == "ab"就是false。

經(jīng)典面試題:

String s1 = "a";
String s2 = "b";
String s3 = "a" + "b"; // ab
String s4 = s1 + s2; // new String("ab")
String s5 = "ab";
String s6 = s4.intern();
// 問
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // true
System.out.println(s3 == s6); // true
String x2 = new String("c") + new String("d"); // new String("cd")
String x1 = "cd"; // "cd"
x2.intern();
System.out.println(x1 == x2); // false

// 問,如果調(diào)換了x1,x2的位置呢?如果是jdk1.6呢?
String x2 = new String("c") + new String("d"); // new String("cd")
x2.intern(); //先把“ab”入常量池
String x1 = "cd"; 
System.out.println(x1 == x2); 
// 此時對于JDK1.8-true,對于JDK1.6-false

(6)StringTable的位置

對于JDK1.6

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

對于JDK1.8

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?那么能通過代碼直觀上體現(xiàn)出StringTable的位置嗎?

import java.util.ArrayList;
import java.util.List;

public class Demo1_6 {

    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        int i = 0;
        try {
            for (int j = 0; j < 260000; j++) {
                list.add(String.valueOf(j).intern());
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

對于JDK1.6把永久代的內(nèi)存設(shè)置小一點:-XX:MaxPermSize=10m

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

對于JDK1.8把堆的內(nèi)存設(shè)置小一點:-Xmx10m,此時并沒有提示堆內(nèi)存不足錯誤;下面的提示表示使用98%的精力去回收,但是值回收了2%,就會報這個錯誤提示。

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

此時需要在加上一個參數(shù),關(guān)閉這個提示?-Xmx10m -XX:-UseGCOverheadLimit

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(7)StringTable的垃圾回收機制

StringTable也是受到垃圾回收機制的管理的,當(dāng)內(nèi)存空間不足時,StringTable中那些還沒有被引用的字符串常量就會被垃圾回收器回收!

設(shè)置參數(shù):-Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc

-Xmx10m:設(shè)置虛擬機堆內(nèi)存的最大值;

-XX:+PrintStringTableStatistics:打印字符串表的統(tǒng)計信息;

-XX:+PrintGCDetails -verbose:gc:打印垃圾回收的一些信息(若發(fā)生了垃圾回收的話);

package cn.itcast.jvm.t1.stringtable;

import java.util.ArrayList;
import java.util.List;

public class Demo1_7 {
    public static void main(String[] args) throws InterruptedException {
        int i = 0;
        try {
            for (int j = 0; j < 100000; j++) { // j=100, j=10000
                String.valueOf(j).intern(); // 入池
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }

    }
}

①先不添加循環(huán)代碼,此時查看StringTable的存儲情況

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②若循環(huán)100次,此時還沒有超過堆內(nèi)存的大小,不會觸發(fā)垃圾回收機制

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

③若循環(huán)10000次,此時已經(jīng)超過堆內(nèi)存的大小,會觸發(fā)垃圾回收機制進行回收

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?啟動了垃圾回收機制的打印信息:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(8)StringTable性能調(diào)優(yōu)

方法1:調(diào)整 -XX:StringTableSize = 桶個數(shù)

StringTable的底層是一個哈希表(數(shù)組+鏈表),哈希表的性能是和它的大小密切相關(guān)的:如果哈希表桶的個數(shù)比較多,元素就會比較分散,哈希碰撞的幾率就會減少,查找的速率也會變快。如果桶的個數(shù)較少,哈希碰撞的幾率就會增高,導(dǎo)致鏈表比較長,查找的速度就會受到影響!

package cn.itcast.jvm.t1.stringtable;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 演示串池大小對性能的影響
 * -Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009
 */
public class Demo1_24 {

    public static void main(String[] args) throws IOException {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern(); // 入串池
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000); // 毫秒
        }


    }
}

所謂的調(diào)優(yōu),最主要的就是調(diào)整桶的個數(shù) -XX:StringTableSize = 桶?數(shù),不設(shè)置虛擬機的內(nèi)存的最大值,對于四萬多個數(shù)據(jù)可以輕松入池!

-XX:StringTableSize = 200000,把桶的個數(shù)調(diào)整為200000

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②不加-XX:StringTableSize這個參數(shù),使用的默認桶大小60013

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

結(jié)論:桶的個數(shù)越小,耗費的時間最多;并且最小的桶個數(shù)是1009!

方法二:考慮將字符串對象是否入池

假設(shè)現(xiàn)在有大量的字符串對象被創(chuàng)建,例如:linux.words文件中有4.8萬個串,循壞10次,對比入池與不如池的內(nèi)存使用情況。

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

public class Demo1_25 {

    public static void main(String[] args) throws IOException {

        List<String> address = new ArrayList<>();
        System.in.read();
        for (int i = 0; i < 10; i++) {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
                String line = null;
                long start = System.nanoTime();
                while (true) {
                    line = reader.readLine();
                    if(line == null) {
                        break;
                    }
                    // 不入池
                    address.add(line);
                    // 入池
                    address.add(line.intern());
                }
                System.out.println("cost:" +(System.nanoTime()-start)/1000000);
            }
        }
        System.in.read();


    }
}

①address.add(line)不入池,此時相當(dāng)于有48萬個數(shù)據(jù)被添加到List集合當(dāng)中。

使用jvisualvm工具,選擇抽樣器:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

對內(nèi)存的占用進行圖形化的展示:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

讀取之前,此時字符串占用的內(nèi)存大概是1M左右:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

?讀取之后,此時字符串占用的內(nèi)存大概是110M左右:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

②address.add(line.intern())入池,入池以后,后面循環(huán)9次的數(shù)據(jù)都是重復(fù)的,都是直接使用只有第一次入池的數(shù)據(jù)即可,此時讀取之后,String+創(chuàng)建的char數(shù)組也才40M不到,大大節(jié)省了堆內(nèi)存空間!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

三:直接內(nèi)存

(1)定義

直接內(nèi)存不屬于Java虛擬機里面的內(nèi)存,是操作系統(tǒng)的內(nèi)存!

直接內(nèi)存:DirectMemory

①常見于NIO操作時 , 用于數(shù)據(jù)緩沖區(qū);?

②分配回收成本較高 , 但讀寫性能高;?

③不受JVM內(nèi)存回收管理;

?案例:使用傳統(tǒng)的IO流和直接內(nèi)存進行比較

package cn.itcast.jvm.t1.direct;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 演示 ByteBuffer 作用
 */
public class Demo1_9 {
    static final String FROM = "E:\\編程資料\\text.txt";
    static final String TO = "E:\\a.txt";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        io(); // io 用時:3秒左右
        directBuffer(); // directBuffer 用時:1秒左右
    }
    // 使用直接內(nèi)存的方式
    private static void directBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
             FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb);
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("directBuffer 用時:" + (end - start) / 1000_000.0);
    }
    // 傳統(tǒng)的IO流
    private static void io() {
        long start = System.nanoTime();
        try (FileInputStream from = new FileInputStream(FROM);
             FileOutputStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];
            while (true) {
                int len = from.read(buf);
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        System.out.println("io 用時:" + (end - start) / 1000_000.0);
    }
}

我們會發(fā)現(xiàn),使用直接內(nèi)存ByteBuffer比傳統(tǒng)的IO流拷貝文件(特別是大文件)的速度明顯快很多,就從文件的讀寫過程進行分析!

對于傳統(tǒng)的IO流:

Java本身并不具備讀寫磁盤的能力,必須調(diào)用操作系統(tǒng)提供的函數(shù);就是從Java的方法調(diào)用到本地的方法;此時CPU會從用戶態(tài)切換到內(nèi)核態(tài);此時就可以讀取磁盤文件的內(nèi)容,此時會在操作系統(tǒng)中劃出來一層緩沖區(qū)(系統(tǒng)緩沖區(qū)),磁盤的內(nèi)容就會先讀取到這個系統(tǒng)緩沖區(qū)(分次讀取,并且Java的代碼是不能讀取系統(tǒng)緩沖區(qū)的內(nèi)容的),此時Java也會在堆內(nèi)存中分配一塊Java的緩沖區(qū);Java要想讀取到數(shù)據(jù),必須先從系統(tǒng)緩存數(shù)據(jù)讀入到Java緩沖區(qū);兩塊緩沖區(qū),相當(dāng)于讀取的時候必須存兩份(造成不必要的數(shù)據(jù)復(fù)制),效率較低!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

對于直接內(nèi)存:

當(dāng)ByteBuffer調(diào)用allocateDirect方法時,會在操作系統(tǒng)間劃出一塊緩沖區(qū)(direct memory),這塊區(qū)域Java代碼是可以直接訪問的;這塊內(nèi)存無論是操作系統(tǒng)還是Java代碼都是可以直接訪問的,共享的一塊內(nèi)存區(qū)域;只有一次緩沖區(qū)的讀入,效率較高!

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

不受JVM內(nèi)存回收管理,所以直接內(nèi)存也會導(dǎo)致內(nèi)存溢出,例:

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;


// 演示直接內(nèi)存溢出
public class Demo1_10 {
    static int _100Mb = 1024 * 1024 * 100;

    public static void main(String[] args) {
        List<ByteBuffer> list = new ArrayList<>();
        int i = 0;
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);
                list.add(byteBuffer);
                i++;
            }
        } finally {
            System.out.println(i);
        }
        // 方法區(qū)是jvm規(guī)范, jdk6 中對方法區(qū)的實現(xiàn)稱為永久代
        //                  jdk8 對方法區(qū)的實現(xiàn)稱為元空間
    }
}

直接內(nèi)存使用操作不當(dāng),也會導(dǎo)致內(nèi)存溢出:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

(2)分配和回收原理

例:

public class Test {
    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb); // 分配1G的空間
        System.out.println("分配完畢...");
        System.in.read();
        System.out.println("開始釋放...");
        byteBuffer = null; // 空引用
        System.gc(); // 啟動垃圾回收
        System.in.read();
    }
}

查看任務(wù)管理器,分配1G:

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

查看任務(wù)管理器,置為null,啟動垃圾回收機制,發(fā)現(xiàn)竟然被回收了!前面不是說直接內(nèi)存不受JVM內(nèi)存回收管理嗎?為什么垃圾回收之后,直接內(nèi)存就被回收釋放了?

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

這就需要解釋一下直接內(nèi)存的釋放原理:首先通過某種方式獲取unsafe對象,通過unsafe對象就可以完成直接內(nèi)存的分配和回收!

注:對于直接內(nèi)存的監(jiān)控,就不能使用IDEA中的那些監(jiān)控工具,需要看任務(wù)管理器中的進程

分配內(nèi)存:

long base = unsafe.allocateMemory(_1Gb);
unsafe.setMemory(base, _1Gb, (byte) 0);

釋放內(nèi)存:

unsafe.freeMemory(base);

ByteBuffer.allocateDirect方法底層是使用了ByteBu?er的實現(xiàn)類DirectByteBuffer

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

DirectByteBuffer的構(gòu)造器中就調(diào)用了unsafe的allocateMemory方法對直接內(nèi)存進行分配

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

DirectByteBuffer的構(gòu)造器中內(nèi)部還使用了 Cleaner (虛引用)來監(jiān)測 ByteBu?er 對象,一旦 ByteBu?er對象被垃圾回收,那么就會由 ReferenceHandler 線程通過Cleaner的clean方法去執(zhí)行任務(wù)對象Deallocator,任務(wù)對象在調(diào)用unsafe對象的freeMemory 來釋放直接內(nèi)存

Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存

-XX:+DisableExplicitGC:禁用顯式回收對直接內(nèi)存的影響,就是讓System.gc()無效,但是此時就會影響直接內(nèi)存的釋放,我們就可以使用unsafe對象手動釋放直接內(nèi)存!文章來源地址http://www.zghlxwxcb.cn/news/detail-451808.html

到了這里,關(guān)于Java虛擬機快速入門 | JVM引言、JVM內(nèi)存結(jié)構(gòu)、直接內(nèi)存的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • java八股文面試[JVM]——JVM內(nèi)存結(jié)構(gòu)

    java八股文面試[JVM]——JVM內(nèi)存結(jié)構(gòu)

    參考: JVM學(xué)習(xí)筆記(一)_卷心菜不卷Iris的博客-CSDN博客 JVM 是運行在操作系統(tǒng)之上的,它與硬件沒有直接的交互 JVM內(nèi)存結(jié)構(gòu): ? 方法區(qū):存儲已被虛擬機加載的類元數(shù)據(jù)信息(元空間) 堆:存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存 虛擬機棧:虛擬機棧描述的是

    2024年02月12日
    瀏覽(25)
  • 深入探討Java虛擬機(JVM):執(zhí)行流程、內(nèi)存管理和垃圾回收機制

    深入探討Java虛擬機(JVM):執(zhí)行流程、內(nèi)存管理和垃圾回收機制

    目錄 什么是JVM? JVM 執(zhí)行流程 JVM 運行時數(shù)據(jù)區(qū) 堆(線程共享) Java虛擬機棧(線程私有) 什么是線程私有? 程序計數(shù)器(線程私有) 方法區(qū)(線程共享) JDK 1.8 元空間的變化 運行時常量池 內(nèi)存布局中的異常問題 1.? Java堆溢出 2.??虛擬機棧和本地方法棧溢出 JVM 類加載 1.

    2024年02月09日
    瀏覽(26)
  • 【JVM故障問題排查心得】「Java技術(shù)體系方向」Java虛擬機內(nèi)存優(yōu)化之虛擬機參數(shù)調(diào)優(yōu)原理介紹

    【JVM故障問題排查心得】「Java技術(shù)體系方向」Java虛擬機內(nèi)存優(yōu)化之虛擬機參數(shù)調(diào)優(yōu)原理介紹

    本文主要針對于綜合層面上進行分析JVM優(yōu)化方案總結(jié)和列舉調(diào)優(yōu)參數(shù)計劃。主要包含: 調(diào)優(yōu)之逃逸分析(棧上分配) 調(diào)優(yōu)之線程局部緩存(TLAB) 調(diào)優(yōu)之G1回收器 -XX:+DoEscapeAnalysis 逃逸分析(Escape Analysis) 逃逸分析的基本行為就是分析對象動態(tài)作用域:當(dāng)一個對象在方法中被定

    2024年01月25日
    瀏覽(20)
  • JVM基礎(chǔ)篇-直接內(nèi)存

    JVM基礎(chǔ)篇-直接內(nèi)存

    什么是直接內(nèi)存? 直接內(nèi)存( 堆外內(nèi)存 ) 指的是 Java 應(yīng)用程序通過直接方式從操作系統(tǒng)中申請的內(nèi)存,這塊內(nèi)存不屬于jvm 傳統(tǒng)方式讀取文件 首先會從用戶態(tài)切換到內(nèi)核態(tài),調(diào)用操作系統(tǒng)函數(shù)從磁盤讀取文件,讀取一部分到操作系統(tǒng)緩沖區(qū)中 然后從內(nèi)核態(tài)切換到用戶態(tài),從系統(tǒng)

    2024年02月13日
    瀏覽(19)
  • Java虛擬機(JVM)垃圾收集器、新生代、老年代、永久代以及內(nèi)存分配策略

    Java虛擬機(JVM)垃圾收集器、新生代、老年代、永久代以及內(nèi)存分配策略

    在 Java 中,堆被劃分成兩個不同的區(qū)域:新生代 ( Young )、老年代 ( Old )。而新生代 ( Young ) 又被劃分為三個區(qū)域:Eden、From Survivor、To Survivor。這樣劃分的目的是為了使 JVM 能夠更好的管理堆內(nèi)存中的對象,包括內(nèi)存的分配以及回收。 新生代中一般保存新出現(xiàn)的對象,所以每次

    2024年02月04日
    瀏覽(27)
  • “深入探究JVM內(nèi)部結(jié)構(gòu)與工作原理:解析Java虛擬機“

    標(biāo)題:深入探究JVM內(nèi)部結(jié)構(gòu)與工作原理 摘要:本文將深入探究Java虛擬機(JVM)的內(nèi)部結(jié)構(gòu)與工作原理。我們將介紹JVM的基本組成部分,包括類加載器、運行時數(shù)據(jù)區(qū)和執(zhí)行引擎。同時,我們將通過一個示例代碼來說明JVM內(nèi)部結(jié)構(gòu)與工作原理的具體應(yīng)用。 介紹: Java虛擬機(

    2024年02月12日
    瀏覽(25)
  • “深入解析JVM內(nèi)部結(jié)構(gòu)與工作原理:揭秘Java虛擬機的奧秘“

    標(biāo)題:深入解析JVM內(nèi)部結(jié)構(gòu)與工作原理:揭秘Java虛擬機的奧秘 摘要:本文將深入探討Java虛擬機(JVM)的內(nèi)部結(jié)構(gòu)和工作原理,幫助開發(fā)者更好地理解JVM的運行機制,從而提高Java程序的性能和穩(wěn)定性。 正文: 一、JVM概述 Java虛擬機(Java Virtual Machine)是Java程序的運行環(huán)境,

    2024年02月11日
    瀏覽(23)
  • JVM——StringTable面試案例+垃圾回收+性能調(diào)優(yōu)+直接內(nèi)存

    JVM——StringTable面試案例+垃圾回收+性能調(diào)優(yōu)+直接內(nèi)存

    JVM——引言+JVM內(nèi)存結(jié)構(gòu)_北嶺山腳鼠鼠的博客-CSDN博客 書接上回內(nèi)存結(jié)構(gòu)——方法區(qū)。 這里常量池是運行時常量池。 intern()方法? intern() 方法用于在運行時將字符串添加到內(nèi)部的字符串池stringtable中,并返回字符串池stringtable中的引用。 返回值 當(dāng)調(diào)用 intern() 方法時,如果字符

    2024年02月12日
    瀏覽(21)
  • JVM內(nèi)存管理、內(nèi)存分區(qū):堆、方法區(qū)、虛擬機棧、本地方法棧、程序計數(shù)器

    JVM內(nèi)存管理、內(nèi)存分區(qū):堆、方法區(qū)、虛擬機棧、本地方法棧、程序計數(shù)器

    線程共享 堆 ????????存放 實例 ,字符串常量(直接引用),靜態(tài)變量,線程分配緩沖區(qū)(TLAB線程私有)。 垃圾收集器 管理的區(qū)域 方法區(qū) ????????非堆,和堆相對的概念。存儲已被虛擬機加載的 類型信息 、常量、靜態(tài)變量、即時編譯器編譯后的代碼緩存等數(shù)據(jù),內(nèi)

    2024年02月10日
    瀏覽(22)
  • 形象談JVM-第四章-JVM內(nèi)存結(jié)構(gòu)

    形象談JVM-第四章-JVM內(nèi)存結(jié)構(gòu)

    給我一個CPU,給我一塊內(nèi)存,我來執(zhí)行一段代碼。 我要如何分配呢? new User(); 這里有一個有一個User類,如果我要new出來User對象,必須先知道它長什么樣子,我先搞一塊區(qū)域出來,把User類的樣子給存下來。 可以把 “User類的樣子” 比作造房子的 “圖紙” 或者 “模板” ;

    2024年02月11日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包