JVM的學(xué)習(xí)小結(jié)

棧和堆的區(qū)別

棧是運(yùn)行時單位岳守,代表著邏輯察藐,內(nèi)含基本數(shù)據(jù)類型和堆中對象引用,所在區(qū)域連續(xù)快骗,沒有碎片;
堆是存儲單位楚昭,代表著數(shù)據(jù),可以多個棧共享(包括成員中基本數(shù)據(jù)類型,引用和引用對象)太雨,所在區(qū)域不連續(xù),會有碎片魁蒜。
區(qū)別:

  • 功能不同
    棧內(nèi)存用來存儲局部變量的基本數(shù)據(jù)類型和對象的引用囊扳,而堆內(nèi)存用來存儲Java中的對象,無論是成員變量兜看,局部變量還是類變量锥咸,它們指向的對象都存儲在堆內(nèi)存中。
  • 共享性不同

棧內(nèi)存是線程私有的
堆內(nèi)存是所有線程共享的

  • 異常錯誤不同

如果棧內(nèi)存或者堆內(nèi)存不足都會拋出異常
椣敢疲空間不足:java.lang.StackOverFlowError.
堆空間不足:Java,lang.OutOfMemoryError.

  • 空間大小
    棧的空間大小遠(yuǎn)遠(yuǎn)小于堆的搏予。

Jvm內(nèi)存模型

方法區(qū)和堆是所有線程共享的內(nèi)存區(qū)域。java虛擬機(jī)棧弧轧,本地方法棧和程序計數(shù)器是線程私有的內(nèi)存區(qū)域雪侥。其中方法區(qū)和堆是垃圾回收的主要區(qū)域,java虛擬機(jī)棧劣针,本地方法和程序計數(shù)器不會發(fā)生垃圾回收校镐。

堆:是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,Java堆是被所有線程共享的一塊內(nèi)存區(qū)域捺典,在虛擬機(jī)啟動時創(chuàng)建鸟廓,此區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存襟己。

方法區(qū):方法區(qū)和堆一樣都是線程共享的區(qū)域引谜,它用于存儲已被虛擬機(jī)加載的類信息,常量擎浴,靜態(tài)變量员咽,即是編譯器編譯后的代碼等數(shù)據(jù)。

java虛擬機(jī)棧:是棧線程私有的贮预,它的生命周期與線程相同贝室,虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型。每一個方法被執(zhí)行的時候都會同時創(chuàng)建一個棧幀仿吞,用于存儲局部變量表滑频,操作棧,動態(tài)鏈接唤冈,方法出口等信息峡迷。每一個方法被調(diào)用直至執(zhí)行完成的過程,就對應(yīng)著一個棧幀在虛擬機(jī)棧中從入棧到出棧的過程。

本地方法棧:本地方法棧與虛擬機(jī)棧所發(fā)揮的作用是非常相似的绘搞,其區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行的Java方法服務(wù)彤避,而本地方法棧則是為虛擬機(jī)使用到的Native方法服務(wù)。

程序計數(shù)器:是一塊是較小的內(nèi)存空間夯辖,它可以看作當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器琉预。字節(jié)碼解析器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,每一個線程都需要一個獨(dú)立的程序計算器蒿褂,各個線程之間計數(shù)器互不影響模孩,獨(dú)立存儲。

Jvm的垃圾回收算法

哪些要回收贮缅?如何判斷對象是否是死忙榨咐?

引用計數(shù)算法

對象中添加一個引用計數(shù)器,每當(dāng)一個地方引用它時谴供,計數(shù)器值就加一块茁;當(dāng)引用失效時,計數(shù)器值就減一桂肌;任何時刻計數(shù)器為零的對象就是不可能再被使用的数焊。

可達(dá)性分析算法

可達(dá)性分析算法的基本思路是通過一系列為"GC Roots" 的根對象作為起始節(jié)點(diǎn)集,從這些節(jié)點(diǎn)開始崎场,根據(jù)引用關(guān)系向下搜索佩耳,通過搜索過程所走過的路徑稱為“引用鏈”,如果這個對象到GC Roots間沒有任何引用鏈相連谭跨,或者用圖論的話就是從GC Roots到這個對象不可達(dá)時干厚,則證明此對象不可能再被使用的。

可以做為GC Roots的對象

  • 在虛擬機(jī)棧中引用的對象螃宙,譬如各個線程被調(diào)用的方法堆蛮瞄,棧中使用帶的參數(shù)、局部變量谆扎、臨時變量等挂捅。
  • 在方法區(qū)中類型靜態(tài)屬性引用的對象,譬如Java類的引用類型靜態(tài)變量堂湖。
  • 在方法區(qū)中引用的對象闲先,譬如字符串常量池里的引用。
  • 在本地方法棧中JNI引用的對象无蜂。
  • Java虛擬機(jī)內(nèi)部的引用伺糠,如基本數(shù)據(jù)可惜對應(yīng)的Class對象,一些常駐的異常對象等酱讶。
  • 所有被同步鎖持有的對象退盯。

垃圾回收算法

標(biāo)記-清除算法

首先標(biāo)記出所有需要回收的對象彼乌,在標(biāo)記完成后泻肯,統(tǒng)一回收掉所有被標(biāo)記的對象渊迁,也可以反過來,標(biāo)記存活的對象灶挟,統(tǒng)一回收所有未被標(biāo)記的對象琉朽。這個算法有兩個主要的缺點(diǎn):第一個是執(zhí)行效率不穩(wěn)定,如果Java堆中包含有大量的對象稚铣,其中大部分是需要被回收的箱叁,這是必須進(jìn)行大量標(biāo)記和清除的動作,導(dǎo)致標(biāo)記和清除兩個過程的執(zhí)行效率都隨對象數(shù)量增長而降低惕医;第二個是內(nèi)存空間的碎化問題耕漱,標(biāo)記、清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片抬伺,空間碎片太多可能會導(dǎo)致當(dāng)以后在程序運(yùn)行過程中需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作螟够。

標(biāo)記-復(fù)制算法

它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊峡钓,這一塊的內(nèi)存用完了妓笙,就將還存活的著的對象復(fù)制到另一塊上面,然后再把自己使用過的內(nèi)存空間一次清理掉能岩。如果內(nèi)存中多數(shù)對象都是存活的寞宫,這種算法將會產(chǎn)生大量的內(nèi)存復(fù)制的開銷,但是對于大多數(shù)對象都是可回收的情況拉鹃,算法需要復(fù)制的就是占少數(shù)的存活對象辈赋,而且每次都是針對整個半?yún)^(qū)進(jìn)行內(nèi)存回收,分配內(nèi)存時也就不需要考慮空間碎片的復(fù)雜情況膏燕。但是這種回收算法的代價是將可用內(nèi)存縮小為了原本的一半炭庙,造成空間的浪費(fèi)。適合用于新生代煌寇。

標(biāo)記-整理算法

首先標(biāo)記處所有需要回收的對象焕蹄,在標(biāo)記完成后,讓所有存活的對象都向內(nèi)存空間的一端移動阀溶,然后直接清理掉邊界以外的內(nèi)存腻脏。適合于老年代對象。

類加載過程

類加載的定義:

Java虛擬機(jī)把描述類的數(shù)據(jù)從Class文件
加載到內(nèi)存银锻,并對數(shù)據(jù)進(jìn)行校驗永品,轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型击纬,這個過程被稱作為虛擬機(jī)的類加載機(jī)制鼎姐。
類加載的過程:

image.png

  • 加載

在加載階段,虛擬機(jī)主要完成以下三件事情:

  • 通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
  • 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)
  • 在內(nèi)存中生成一個代表這個類的java.lang.Class對象炕桨,作為這個類的各種數(shù)據(jù)的訪問入口饭尝。
  • 驗證

驗證是連接階段的第一步,目的是確保Class文件的字節(jié)流中包含的信息符合虛擬機(jī)的全部約束要求献宫,保證這些信息被當(dāng)作代碼運(yùn)行后不會危害虛擬機(jī)的安全钥平。驗證字節(jié)碼是Java虛擬機(jī)保護(hù)自身的一項必要措施。驗證階段非常必要姊途,這個階段是否嚴(yán)謹(jǐn)涉瘾,直接決定了Java虛擬機(jī)是否能承受惡意代碼的攻擊。驗證階段主要是對文件格式驗證捷兰,元數(shù)據(jù)驗證立叛,字節(jié)碼驗證和符號引用驗證。

  • 準(zhǔn)備

準(zhǔn)備階段是正式類中定義的變量贡茅,即靜態(tài)變量分配內(nèi)存并設(shè)置變量初始化值的階段囚巴。需要注意的是初始值有以下兩種情況:

public static int value=123;
public static final inr value=123;

第一行代碼在準(zhǔn)備階段過后的賦值是0而不是123,因為這時尚未開始執(zhí)行任何Java方法友扰,如果被final修飾之后彤叉,在準(zhǔn)備階段就會根據(jù)ConstantValue屬性所指定的初始值。

  • 解析

解析階段是Java虛擬機(jī)將常量池的符號引用替換為直接引用的過程村怪。

  • 符號引用:符號引用以一組符號來描述所引用的目標(biāo)秽浇,符號可以是任何形式的字面量,只要使用時無歧義地定位到目標(biāo)即可甚负。符號引用與虛擬機(jī)的內(nèi)存布局無關(guān)柬焕,引用的目標(biāo)不一定是已經(jīng)加載到虛擬機(jī)內(nèi)存當(dāng)中的內(nèi)容。各種虛擬機(jī)實現(xiàn)的內(nèi)存布局可以各不相同梭域,但是它們能接受的符號引用必須都是一直的斑举。因為符號引用的字面量形式明確定義在《Java虛擬機(jī)規(guī)范》的Class文件格式中。
  • 直接引用是可以直接指向目標(biāo)的指針病涨,相對偏移量或者一個能簡接定位到目標(biāo)的句柄富玷。直接引用是和虛擬機(jī)實現(xiàn)內(nèi)存布局直接相關(guān)的,同一個符號引用在不同虛擬機(jī)實例上翻譯出來的 直接引用一般不會相同既穆。
  • 初始化

類的初始化階段是類加載過程的最后一步驟赎懦,直到初始化階段,Java虛擬機(jī)才真正開始執(zhí)行類中編寫的Java程序代碼幻工,將主導(dǎo)權(quán)移交給應(yīng)用程序励两。在準(zhǔn)備階段時,變量已經(jīng)賦過一次系統(tǒng)要求的初始值囊颅,而初始化階段当悔,則會根據(jù)程序通過程序編碼定制的主觀計劃去初始化類變量和其他資源傅瞻。直觀表達(dá)就是初始化階段就是執(zhí)行類構(gòu)造器<clinit>()方法的過程,主要完成靜態(tài)代碼塊執(zhí)行以及靜態(tài)變量的賦值。

類加載器

對于任意一個類盲憎,都必須由加載它的類加載器和這個類本身一起共同確立其在Java虛擬機(jī)中的唯一性嗅骄,每一個類加載器,都擁有一個獨(dú)立的類名稱空間焙畔。 可以參考https://blog.csdn.net/m0_38075425/article/details/81627349

類加載器的介紹

站在Java虛擬機(jī)的角度看,只存在兩種不同的類加載器:一種是啟動類加載器串远,這個類加載器使用C++語言實現(xiàn)宏多,是虛擬機(jī)自身的一部分;另外一種就是其他所有的類加載器澡罚,這些類加載器都由Java語言實現(xiàn)伸但,獨(dú)立存在虛擬機(jī)外部,并且全都繼承自抽象類java.lang.ClassLoader留搔。在JDK8更胖,之前一直都是使用三層類加載器。

  • 啟動類加載器:這個兩類加載器負(fù)責(zé)加載存放在<JAVA_HOME>\lib目錄隔显,或者被-Xbootclasspath參數(shù)所在指定的路徑中存放的却妨,而且是Java虛擬機(jī)能夠識別的類加載到虛擬機(jī)的內(nèi)存中。
  • 擴(kuò)展類加載器:這個類加載器是在類sun.misc.Launcher$ExtClassLoader中以Java代碼的形式實現(xiàn)的括眠。它負(fù)責(zé)加載<JAVA_HOME>\lib\ext目錄中彪标,或者被java.ext.dirs系統(tǒng)變量所指定的路經(jīng)中所有的類庫。
  • 應(yīng)用程序類加載器:這個類加載器由sun.misc.Launcher$AppClassLoader來實現(xiàn)掷豺。由于程序類加載器是ClassLoader類中的getSystemClassLoader()方法的返回值捞烟,所以有些場合中也被它稱為“系統(tǒng)類加載器”。

雙親委派模型

類加載器之間的父子關(guān)系一般不是以繼承的關(guān)系來實現(xiàn)当船,而是通常使用組合關(guān)系來復(fù)用父類加載器的代碼题画。
雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類德频,而是把這個請求委派給父類加載器去完成苍息,每一個層次的類加載器都是如此,因此所有的加載請求最終都是應(yīng)該傳送到最頂層的啟動類加載器中壹置,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個加載請求時档叔,子加載器才會嘗試自己去完成加載。
雙親委派加載器的優(yōu)勢就是:Java中的類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系蒸绩。例如類Java.lang.Object,它存放在rt.jar之中衙四,無論哪一個類加載器需要加載這個類,最終都是委派給處于模型最頂端的啟動類加載器進(jìn)行加載患亿,因此Object類在程序的各種類加載器環(huán)境中都能夠保證是同一個類传蹈。


image.png

Java內(nèi)存模型

  • 主要目的

Java內(nèi)存模型的主要目的是定義程序中各種變量的訪問規(guī)則押逼,即關(guān)注在虛擬機(jī)中把變量值存儲到內(nèi)存和從內(nèi)存中取出來變量值這樣的底層細(xì)節(jié)。此處的變量與Java編程中所說的變量有所區(qū)別惦界,它包括了實例字段挑格,靜態(tài)字段和構(gòu)成數(shù)組對象的元素,但是不包括局部變量和方法參數(shù)沾歪,因為后者是線程私有的漂彤,不會被共享,自然就不存在競爭的問題灾搏。
Java內(nèi)存模型規(guī)定了所有的變量都存儲在主內(nèi)存中挫望。每條線程還有自己的工作內(nèi)存,線程的工作內(nèi)存保存了被線程使用的變量的主存的副本狂窑,線程對變量的所有操作都必須都是在工作內(nèi)存中進(jìn)行的媳板,而不能直接讀寫主內(nèi)存中數(shù)據(jù)。不同線程之間無法直接訪問對方工作內(nèi)存的對象泉哈,線程間變量值的傳遞均需要通過主存來完成蛉幸。

原子性,有序性和可見性

  • 原子性

原子性是指一個操作是不可中斷的丛晦,要么全部執(zhí)行成功要么全部執(zhí)行失敗奕纫,在多線程中一個操作一旦開始,就不會被其他線程所干擾烫沙。但是volatile不能保證原子性若锁。

  • 有序性:

可以總結(jié)為一句話:如果在本地線程內(nèi)觀察,所有操作都是有序的斧吐;如果在一個線程中觀察另一個線程又固,所有線程都是無序的。前半句是指“線程內(nèi)是表現(xiàn)為串行的意義”后半句是指“指令重排序”的現(xiàn)象和“工作內(nèi)存與主內(nèi)存同步延遲”現(xiàn)象煤率。Volatile關(guān)鍵字本身包含了禁止指令重排序的語義仰冠。而sychronied則是由“一個變量在同一時刻只能允許一條線程對其進(jìn)行l(wèi)ock操作〉矗”
禁止指令重排序是指:能夠保證變量的賦值操作的順序與程序代碼中的執(zhí)行順序一致洋只。而普通變量僅保證在改方法執(zhí)行過程中所有依賴賦值結(jié)果的地方都能獲取到正確的結(jié)果。

  • 可見性

可見性是指當(dāng)前一個線程修改了共享變量的值時昼捍,其他線程能夠立即得知這個修改识虚。Java內(nèi)存模型是通過在遍歷修改后將新值同步到主內(nèi)存中,在變量讀取前從主內(nèi)存中刷新變量值妒茬。Volatile保證了新值能夠立即同步到主內(nèi)存中担锤,以及每次使用前立即從主內(nèi)存中刷新。sychronied的可見性是由“對一個變量執(zhí)行unlock操作之前乍钻,必須先把此變量同步回主內(nèi)存中肛循。final的可見性是指:被final修飾的字段在構(gòu)造器中一旦被初始化完成铭腕,并且構(gòu)造器沒有把"this"的引用傳遞出去,那么其他線程中就能看見final字段的值多糠。

可重入性是指當(dāng)線程請求自己持有的對象鎖時累舷,如果請求成功,則說明是可重入鎖夹孔。sychronied是一個可重入鎖被盈,因此在一個線程使用sychronized方法時調(diào)用該對象另一個sychronied方法,即一個線程得到一個對象鎖后再次請求該對象鎖是永遠(yuǎn)可以獲得鎖的搭伤。

synchronized可重入鎖的實現(xiàn)

每個鎖關(guān)聯(lián)一個線程持有者和一個計數(shù)器只怎,當(dāng)計數(shù)器為0表示該鎖沒有被任何線程持有,那么任何線程都可能獲得該鎖而調(diào)用相應(yīng)的方法闷畸。當(dāng)一個線程獲得鎖后尝盼,Jvm會記下持有鎖的線程吞滞,并將計數(shù)器為1佑菩。此時其他線程請求該鎖,則必須等待裁赠,而持有該鎖的線程如果再次請求這個鎖殿漠,可以再次拿到,同時計算器會遞增佩捞。當(dāng)線程退出一個sychronied方法/塊時绞幌,計數(shù)器就會遞減,如果計數(shù)器為0則釋放該鎖一忱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末莲蜘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子帘营,更是在濱河造成了極大的恐慌票渠,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芬迄,死亡現(xiàn)場離奇詭異问顷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)禀梳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門杜窄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人算途,你說我怎么就攤上這事塞耕。” “怎么了嘴瓤?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵荷科,是天一觀的道長唯咬。 經(jīng)常有香客問我,道長畏浆,這世上最難降的妖魔是什么胆胰? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮刻获,結(jié)果婚禮上蜀涨,老公的妹妹穿的比我還像新娘。我一直安慰自己蝎毡,他們只是感情好厚柳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沐兵,像睡著了一般别垮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扎谎,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天碳想,我揣著相機(jī)與錄音,去河邊找鬼毁靶。 笑死胧奔,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的预吆。 我是一名探鬼主播龙填,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拐叉!你這毒婦竟也來了岩遗?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤凤瘦,失蹤者是張志新(化名)和其女友劉穎宿礁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廷粒,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡窘拯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坝茎。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涤姊。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖嗤放,靈堂內(nèi)的尸體忽然破棺而出思喊,到底是詐尸還是另有隱情,我是刑警寧澤次酌,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布恨课,位于F島的核電站舆乔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剂公。R本人自食惡果不足惜希俩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纲辽。 院中可真熱鬧颜武,春花似錦、人聲如沸拖吼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吊档。三九已至篙议,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怠硼,已是汗流浹背鬼贱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拒名,地道東北人吩愧。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓芋酌,卻偏偏與公主長得像增显,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子脐帝,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345