JVM 基礎

鞏固基礎,砥礪前行 。
只有不斷重復,才能做到超越自己。
能堅持把簡單的事情做到極致,也是不容易的。

JVM 類加載機制

JVM 類加載機制分為五個部分:加載,驗證,準備,解析,初始化,下面我們就分別來看一下這五個過程

加載

加載是類加載過程中的一個階段,這個階段會在內存中生成一個代表這個類的java.lang.Class 對象,作為方法區這個類的各種數據的入口。注意這里不一定非得要從一個Class 文件獲取,這里既可以從 ZIP 包中讀取(比如從jar 包和 war 包中讀取),也可以在運行時計算生成(動態代理), 也可以由其它文件生成(比如將 JSP 文件轉換成對應的Class 類)

驗證

這一階段的主要目的是為了確保 Class 文件的字節流中包含的信息是否符合當前虛擬機的要求,并且不會危害虛擬機自身的安全

準備

準備階段是正式為類變量分配內存并設置類變量的初始值階段,即在方法區中分配這些變量所使用的內存空間

解析

解析階段是指虛擬機將常量池中的符號引用替換為直接引用的過程

符號引用

符號引用與虛擬機實現的布局無關,引用的目標并不一定要已經加載到內存中。各種虛擬機實現的內存布局可以各不相同,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在 Java 虛擬機規范的Class 文件格式中

直接引用

直接引用可以是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。如果有了直接引用,那引用的目標必定已經在內存中存在

初始化

初始化階段是類加載最后一個階段,前面的類加載階段之后,除了在加載階段可以自定義類加載器以外,其它操作都由JVM 主導。到了初始階段,才開始真正執行類中定義的Java 程序代碼

類構造器

初始化階段是執行類構造器方法的過程。方法是由編譯器自動收集類中的類變量的賦值操作和靜態語句塊中的語句合并而成的。虛擬機會保證子方法執行之前,父類的方法已經執行完畢,如果一個類中沒有對靜態變量賦值也沒有靜態語句塊,那么編譯器可以不為這個類生成()方法。

注意以下幾種情況不會執行類初始化:

  1. 通過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。
  2. 定義對象數組,不會觸發該類的初始化
  3. 常量在編譯期間會存入調用類的常量池中,本質上并沒有直接引用定義常量的類,不會觸發定義常量所在的類
  4. 通過類名獲取Class 對象,不會觸發類的初始化。
  5. 通過Class.forName 加載指定類時,如果指定參數 initialize 為false 時,也不會觸發類初始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。
    通過ClassLoader 默認的loadClass 方法,也不會觸發初始化動作。

虛擬機類加載機制(類加載過程)

加載過程

接下來我們詳細講解一下加我群里一中類加載的全過程,也就是加載,驗證,準備解析和初始化這五個階段所執行的具體動作。

加載

加載是類加載過程的一個階段,希望讀者沒有混淆。這兩個看起來好像是的名詞,在家找階段虛擬機需要完成下面三件事。
1.通過一個類的全限定名來獲取定義此類的二進制字節流。
2.將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3.在內存中生成一個代表這個類。的對象作為方法區這個類的各種數據的訪問入口
虛擬機規范的這三點要求其實并不算具體,因此虛擬機實現與具體應用的靈活度。都是相當大的,例如通過一個類的全限定名來。獲取定義此類的二進制字節流這條它并沒有指定二進制字節流。一定要從一個class文件中獲取,準確的說是根本沒有指定從哪里獲取,怎樣獲取。虛擬機設計團隊在加載階段。搭建了一個相當開放廣闊的舞臺,Java發展歷程中,充分創造力的開發人員則在這個舞臺上玩出了各種花樣。舉足輕重的Java技術都建立在這一基礎之上,例如,
1)從zip包中讀取這種很常見,最終日后的炸ear。war格式的基礎
2)從網絡中獲取這種場景,最典型的就是applied
3)運行時計算生成這種場景使用的最多的就是動態代理技術。在java.lang.reflect.proxy中,就是使用了Process generator店generator proxy class。來為特定接口生成顯示為$ proxy代理類的二進制字節流。
4)有其他文件產生。典型的應用就是JSP應用,既有這次文件上傳對應的class類。
5)從數據庫中讀取這種場景相對少見,例如有些中間件服務器。可以選擇把應用程序安裝到數據庫中來完成程序代碼。在集群間的發放。

相當于類加載過程的其他階段,一個非數組類的加載階段,準確的說是加載階段中獲取類的二進制。自己留的動作是開發人員可控性最強的,因為加載階段既可以使用系統提供的引導類加載器來完成,也可以由用戶自定義的類加載器去完成。開發人員可以通過自定義的類加載器去控制自己留的獲取方式。G重寫一個類加載器的load class方法。
對數組類而言,情況就有所不同,數組類本身不通過類加載器創建,它是由加瓦虛擬機直接創建的,但數組類與類加載器仍然有很密切的關系,因為數組類的元素類型。指的是數組,去掉所有維度的類型,最終是要考慮加載器去創建。一個數組類創建過程就要遵循以下原則。
1)如果數組的組件類型是引用類型,那就要遞歸采用綁結。中定義的加載過程去加載這個組件類型。數組c將在加載該組建類型的類加載器的。那名空間上被標識
2)如果數組的組件類型不是引用類型千瓦虛擬監會把數組c標記為與引導類加載器關聯。
3)數組類型的可見心與他的組件類型的可見性一致,如果組件類型不是引用類型那出組的。類的可見性被默認為public。

加載階段完成好后,虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區之中。方法句中的數據存儲格式由虛擬機實現自新定義。胸肌規范,未規定此區域的具體數據結構,然后在內存中實例化一個java.lang.class類的對象。并沒有明確規定是在Java堆中。對于hot stop虛擬機而言,Class對象比較特殊,它雖然是對象,但是存儲在方法區里面。這個對象將作為程序訪問方法區中的這些類型數據的外部接口。加載階段與鏈接階段的部分內容是交叉進行的。加載階段尚未完成,鏈接階段可能已經開始,但這些加載加載階段之中進行的動作仍然屬于鏈接階段的內容,這兩個階段的開始時間仍然保持著固定的先后順序。

虛擬機的加載機制(驗證)

驗證試驗階段的第一步,這一步的目的是為了確保class文件的字節流包含的信息符合當前虛擬機的要求。而且不會危害虛擬機自身的安全

java語言本身是相對安全的語言使用純粹加瓦語言的代碼。無法做到。諸如如何訪問數組邊界以外的數據,將一個對象轉化為它。并為實現的類型跳轉到不存在的代碼行之類的事情。如果這樣做了,邊界將拒絕便宜,但前面杰說過,class文件并不一定要求Java源碼編譯而來。可以使用任何途徑產生甚至包含用16進制編輯器直接編寫下來的class文件。再次解碼語言層面上刪除Java代碼無法做到的事情是可以實現的,至少遇上。可以表達出來的虛擬機如果不檢查輸入的字節流,對其完全信任的話,很可能因為載入有害的字節流而導致系統崩潰,所以驗證是虛擬機對自身保護的一項重要工作

驗證階段是非常重要的,這個階段是否嚴謹直接決定了交往虛擬機是否傳授惡意代碼攻擊,從執行性能的角度講講驗證階段的工作量是虛擬機的類加載此系統中賬了相當大的一部分。對這個機制的限制知道還比較籠統的規范中列舉了一些class文件格式的靜態和結構化約束,如果驗證到輸入的字節流不符合class文件格式的約束,虛擬機就會拋出一個Java。亂點verify,exception異常或此類異常,但具體應當檢查哪些方面,如何檢查,核實,檢查都沒有足夠的要求和明確的說明,直到2011年發布的Java虛擬機規范Java se第七版。大幅增加了描述驗證過程的篇幅,從不到十頁增加到100。30頁,這時約束和驗證規則才變得具體起來,受篇幅所限。當初無法逐條規則去剪剪,當從整體上去看,驗證階段大致會完成下面四個階段的檢驗動作。文件格式檢驗、元數據檢驗、字節碼檢驗、符號引用檢驗。

文件格式檢驗

第一階段要驗證自己留是否符合class文件格式的規范,并且能被當前版本的虛擬機處理,這一階段可能包含下面這些驗證點。
1.是否以魔數xcafebabe開頭
2.主次版本號是否在當前虛擬機處理范圍之內。
3.常量值的常量中是否有不被支持的常量類型
4.指向常量的各種索引值中是否有只限不存在的常量或不符合類型的常量
5.Class文件中各個部分及文件本身是否有被刪除或附加的其他信息。

實際上,第一階段的驗證點遠不止如此,上面只是從hot stop虛擬機。源碼中宅出的一小部分內容,該驗證階段的主要目的是保證輸入的字節流。藍正確的解析并存儲于方法區之內,格式上符合描述一個Java類型信息的要求這階段的驗證是基于二進制字節流進行的。只有通過了這個階段的驗證后,字節流才會進入內存的方法區中進行存儲。所以后面的三個驗證階段全部是基于方法區的存儲結構進行的。不會再直接操作字節流

元數據驗證

第二階段是對字節碼描述的信息進行語義分析,一保障其描述的信息符合Java。語言規范的要求這個階段可能包含的驗證點如下
1.這個類是否有父類,除了object這個他之外所有的累都有父類。
2.這個類的父類是否繼承了不允許被繼承的類?被final修飾的類
3.如果這個類不是抽象類,是否實現了其父類或接口之中要求實現的所有方法
4.類中的字段方法是否與父類產生矛盾,如果覆蓋了父類的final字段。或者出現了不符合規則的方法重載,例如方法,參數都一致,當返回類型卻不相同等

第二階段主要的目的是對類的元數據信息進行語義檢驗保證不存在不符合java語言規范的元數據信息

字節碼驗證

第三階段是整個驗證過程中最復雜的一個階段。主要的目的是通過數據流和控制流分析確定程序語義是合法的,符合邏輯的。在第二階段,對元數據信息中的數據類型做完校驗后,這個階段間對類的方法體進行就業分析保障被救援的類的方法,在運行時不會做出危險虛擬機的安全事件,例如,
1.保證任意時刻操作數棧的數據類型和指令編代碼。序列都能配合工作,例如不會出現類似的情況。在操作站放了一個int類型的數據,使用時確按照long來加載到本地變量表中。
2.保證跳轉指令不會跳轉到方法體以外的字節碼的指令上。
3.保障方法體重的類型狀況是有效的,例如可以把一個子類對象賦值給父類數據類型。這是安全的,當時把父類對象賦值給此類數據類型,甚至把對象賦值給它,毫無繼承關系完全不相干的一個數據類型。這是危險合不合法的
如果一個類方法體的字節碼沒有通過字節碼驗證,那肯定是有問題的。但如果一個方法體通過了字節碼驗證,也不能說明其一定就是安全的。即使自己碼驗證之中存在了大量的檢查,也不能保證這一點,這里涉及那離散數學中一個很著名的問題Halting Problem。通俗一點說,通過查數據校驗程序邏輯是無法做到絕對準確的,不能通過程序準確的檢查出程序是否能在有限時間之內結束運行。

對于數據流驗證的高復雜性迅疾設計團隊為了避免過多的時間how在自己把驗證階段在這dk1.6之后的加我c編譯器和Java虛擬機中進行了一鍵優化給封話題的code屬性。屬性表中增加了一下名為stick map table的屬性。這項屬性描述了方法體中所有的基本塊兒。按照流程,拆封的代碼塊開始本地變量表和操作數棧。亦友的狀態再次解綁,驗證期間就不需要根據程序推導這個狀態的合法性。只需要檢查stick maple table屬性中的記錄是否合法即可。這樣直接把驗證的類型推導變成了類型檢查,從而節約了一些時間。

理論上的stick map table屬性也存在錯誤或被篡改的可能。所以,是否有可能在惡意篡改了code屬性的同時,也生成相應的stick map table屬性來騙過迅疾的內心。校驗則是虛擬機設計者值得思考的問題

在jdk1.6的hot spot報虛擬機提供了-XX: UseSplitverifer選項來關閉此優化或者使用參數。-XX: FailOverToOldVerfier要求在類型校驗失敗的時候退回到舊的類型,推導方式進行校驗。而在這第一個1.7之后,對于主版本大于50的class文件,使用類型檢查來完成數據流分析。校驗則是唯一的選擇不允許再退回到類型推導的方式

符號引用驗證

最后一個階段的校驗發生在迅即將符號引用轉換為直接引用的時候,這個轉化動作間在鏈接的第三階段解析階段中發生。符號引用驗證可以看做是對類自身以外的信息進行匹配性。檢驗。通常需要檢驗下列的內容
1.符號應用中通常字符串描述的全限地名是否找到對應的類。
2.在指定類中是否存在符號方法的字段描述符以及簡單米長所描述的方法和字段。
3.符號引用中的類字段方法的訪問c是否可以被當前類訪問

符號引用驗證的目的是保證解析動作正常執行,如無法通過符號引用驗證。那么將會拋出一個異常的子類。
對于虛擬機的類加載機制來說,驗證階段是一個非常重要的,但不是一定必要的階段,如果所運行的全部代碼都已經被反復使用和驗證過,那么在實施階段就可以考慮使用參數-Xverify: none倉鼠來關閉大部分的內件驗措施,以縮短虛擬機類加載的時間。

虛擬機類加載機制(準備)

準備階段是正式為類變量分配內存空間。并設置類變量初始值的階段,這些變量所使用的內存將在方法區中進行分配,這個階段中有兩個容易產生混淆的概念,要強調一下,首先,這時候進行的內存分配僅包含類變量被static修飾的變量。而不包含實例變量,實例變量將會在對象實例化時隨著對象一起分配到甲瓦堆中。其次,這里所說的初始化通常情況下是指數據類型的零值。
上面提到在通常情況下初始值是零值,那么相對的會有一些特殊情況,如果類字段的屬性表中存在constant value屬性。那么在準備階段,變量value就會被初始化為constant value屬性所指定的值。如,被final修飾。

虛擬機類加載機制(初始化)

虛擬機類加載機制

代碼編譯的結果從本地機器碼轉變為字節碼是存儲格式發展的一小步,卻是編程語言發展的一大步。

概述

上一章我們了解了class文件存儲格式的具體細節,在class文件中描述的各種信息。最終都需要加載到虛擬機中,之后才能運行和使用,而虛擬機如何加載這些class文件,class文件中的信息進入到虛擬機后會發生什么變化,這些都是本章要進行講解的內容。
去你雞巴描述類的數據從class文件加載到內存,并對數據進行校驗,轉換,解析和初始化最終形成。可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。
與那些在編譯時需要進行鏈接工作的語言不同在Java語言里類型的加載。鏈接和初始化過程都是在程序運行期間完成的,這種策略雖然會令那加載時稍微增加一些性能開銷,倒是會為Java應用程序提供。高度的靈活性加我李天生可以動態擴展的語言特性,就是依賴運行期動態加載和動態鏈接這兩個特性實現的。例如,如果編寫一個面向接口的應用程序,可以等到運行時在指定企業實際的實現類。用戶可以通過Java預定義和自定義類在加載器,讓一個本地的應用程序可以在運行時從網絡或其他地方加載一個二進制流作為程序代碼的一部分。這種組裝應用程序的方式目前已經廣泛應用于加我程序之中。從最基本的JSP applied要相對復雜的os gi技術。都使用了Java語言運行期內加載的特性。
為了避免語言表達中可能存在偏差,在本章正式開始之前,筆者先設立兩個語言上的約定,第一,在實際情況中,每個class文件都有可能代表著Java語言中的一個接口或類。課文中對類的描述都包含了類和接口的可能性,而對類和接口需要分開描述的場景會特別指明,第二,于前面介紹class文件格。而是時約定一致。筆者本站所提到的class文件并非指某個存在于具體磁盤中的文件,這里所說的class文件應當是一串二進制的自己流。無論以任何形式存在都可以。

類的加載時機

內存被加載到虛擬機內存中開始到卸載出內存為止,它的整個生命周期包括加載,驗證,準備解析,初始化,使用和卸載七個階段。其中驗證準備解析。三個部分統稱為鏈接。
加載,驗證,準備,初始化和卸載這五個階段的順序是確定的,類的加載過程必須按照這種順序按部就班的開始,而解析階段則不一定。他在某些情況下可以在初始化之后進行開始,這是為了支持Java語言的運行,是綁定也稱為動態綁定。或往期綁定注意,這里筆者寫的是按部就班的開始,而不是按部就班的運行或完成,強調這點是因為這些階段通常都是相互交叉混合進行的,通常會在一個階段執行的過程中調用激活另外一個階段。

什么情況下開始類加載過程的第一個階段?加載千瓦虛擬機規范中并沒有進行強制約束,這點可以。作為虛擬機的具體實現來自有把握當做于初始化階段,虛擬機規范則是嚴格規定了,有且只有五種情況,必須立即對。類進行初始化,而加載,驗證準備自然需要在此之前開始。
1.遇到new get static,put static或Invoke static這四條字節碼指令時,如果累沒有進行過初始化。則需要先觸發及初始化發生這四條指令的最常見的Java代碼,團結是使用new關鍵字實例化對象的時候。讀取或設置一個類的靜態字段被final修飾,已在編譯器把結果放入常量池的靜態字段除外。以及調用一個類的靜態方法的時候。
2.使用Java.lang.reflect包的方法對類進行反射時調用的時候。如果累沒有進行過初始化,則需要相觸發及初始化。
3.當初始化一個類的時候,如果發現其父類還沒有進行過的初始化。則需要先觸發其父類的初始化。
4.當虛擬機啟動時,用戶需要指定一個執行的主類,包含內方法的那個類。虛擬機會先初始化這個主類
5.當時用JDK七的動態語言支持時,如果一個Java Lang, dear invoke method handler.實力最后的解析結果是Ref,get static ref put static if evoke static的方法句柄.并且這個方法句柄所對應的類沒有進行過初始化,則需要先觸發其初始化。
對于這五種住發類進行初始化的場景,虛擬機規范中使用了一個很強烈的限定語。有且只有這五種場景中的行為稱為對一個類進行。主動引用,除此之外,所有引用類的方法都不會觸發。初始化被稱為被動引用。

JVM之Java中的四種引用類型

在java中一切都是對象,對象的操作是通過對象的引用實現的,java中的引用對象類型有四種:強、軟、弱、虛。

  1. 強:在java中最常見的及時前引用,再把一個對象賦值給一個引用變量時,這個引用變量就是一個強引用。有強引用的對象一定為可達性狀態,所以不會被垃圾回收機制回收,因此,強引用就是造成java內存溢出的主要原因。
  2. 軟:阮引用通過softreference類實現,如果一個低下只有軟引用,則在系統內存空間不足時該對象將被回收
  3. 弱:弱引用通過weakreference類實現,如果一個對象只有弱引用,則在垃圾回收過程中一定會被回收
  4. 虛:虛引用通過phantomreference類實現,虛引用和引用對象聯合使用,主要用于追蹤對象的垃圾回收狀態

JVM之類加載器和雙親委派機制

JVM之類加載器

虛擬機設計團隊把加載動作放到 JVM 外部實現,以便讓應用程序決定如何獲取所需的類,JVM 提供了 3 種類加載器

啟動類加載器(Bootstrap ClassLoader)

負責加載 JAVA_HOME\lib 目錄中的,或通過-Xbootclasspath 參數指定路徑中的,且被虛擬機認可(按文件名識別,如 rt.jar)的類

擴展類加載器(Extension ClassLoader)

負責加載 JAVA_HOME\lib\ext 目錄中的,或通過java.ext.dirs 系統變量指定路徑中的類庫

應用程序類加載器(Application ClassLoader):

負責加載用戶路徑(classpath)上的類庫。JVM 通過雙親委派模型進行類的加載,當然我們也可以通過繼承java.lang.ClassLoader 實現自定義的類加載器

雙親委派機制

當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啟動類加載其中, 只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的
Class),子類加載器才會嘗試自己去加載。
采用雙親委派的一個好處是比如加載位于 rt.jar 包中的類 java.lang.Object,不管是哪個加載器加載這個類,最終都是委托給頂層的啟動類加載器進行加載,這樣就保證了使用不同的類加載器最終得到的都是同樣一個 Object 對象。

Java垃圾回收與算法

在這里插入圖片描述

如何確定垃圾?

java采用引用計數法和可達性分析算法來確定對象是否應該被護手,其中引用激素分容易產生循環依賴的問題,可達性分析算法通過根據搜索算法來實現。根據搜索算法可以一系列gc roots 的點作為起點向下搜索,在一個對象到任何gc roots都沒有引用鏈相連時,說明對象已經死亡,根據搜索算法主要針對棧中的引用,方法區中的靜態變量引用和jni中的引用展開分析。

引用計數法

在Java中如果要操作對象,就必須先獲得該對象的引用,因此可以通過引用計數法來判斷一個對象是否可以被回收。在為對象添加一個引用是,引用計數+1,在為對象刪除一個引用時,減少一個引用。如果一個對象的引用計數為0 ,則表示該對象沒有被引用,可以被回收。

引用計數法容易產生循環引用的問題,循環引用值兩個對象互相引用,導致他們的引用一直存在,而不能回收

可達性分析算法

為了解決引用計數法的循環引用的問題,java還采用了可達性分析算法來判斷對象是否可以被回收,具體做法,首先定義一些gc roots 對象,然后以這些對象作為起點向下搜索,如果gcroots 和一個對象直接沒有可達路徑,則稱該對象是不可達的。不可達對象要經過至少兩次標記才能判斷是否可以被回收,如果在兩次標記后該對象仍然不是可達的,則將被垃圾回收期回收。

哪些對象可以作為 gcroots對象呢 ?稍后回復

Java中常用的來及回收算法

Java中常用的垃圾回收算法有標記清除、復制、標記整理、分代回收四種垃圾回收算法

標記清除算法

標記清除算法是基礎的垃圾回收算法,其過程分為標記和清理兩個階段,在標記階段標記所有需要回收的對象,在清除極端可回收的對象并釋放所長用的內存空間。

由于標記清除算法實在清理對象鎖占用的內存空間后并沒有重新整理可用的內存空間,因此如果內存中被護手的小對象過多,則會引起內存碎片化的問題,繼而引起大對象無法獲得連續可用的內存空間問題

復制算法

復制算法為了解決標記清除算法內存碎片化的問題而設計的,復制算法首先將內存活粉兩塊大小相等的內存區域,1區域,2區域,新生成的對象都被存放在1區域,在1區域內的對象存儲忙后會對內存1進行一次標記,并將標記后仍然存活的對象全部復制到區域2中,然后直接清理1區域的內存。

復制算法的內存清理效率高,并且容易實現,但由于同一時刻只有一個內存區域可用,即可用的內存空間被壓縮為原來的一半,因此存在大量的內存空間浪費,同時,在系統中有大量長時間存活的對象是,這些對象將存活在1區域和2區域之間來回復制而影響系統的運行效率。因此該算法只在對象存活時間較短時效率高。

標記整理算法

標記整理算法結合了標記清除算法和復制算法的有點,其標記階段和標記清除算法的標記階段仙童,在標記完成后存活的對象復制到內存的一端,然后清除該端的對象并釋放內存

分代回收算法

無論是標記清除算法、復制算法、標記整理算法,都無法堆所有類型(長生命手氣、短生命周期、大對象、小對象)的對象進行垃圾回收。因此正對不同的對象類型,jvm采用了不同的垃圾回收算法,該算法被稱為分代回收算法

分代回收算法根據對象的不同類型將內存劃分為不同的區域,jvm將對劃分為新生代和老年代,新生代主要存放新生成的對象,其特點是對象數量多但是生命周期短,在每次進行垃圾回收是都有大量的堆被回收,老年代的主要存放大對象和生命走起長的對象,因此可回收的對象相對較少,因此,jvm根據不同的區域的特點選擇不同的算法。

目前,大部分jvm在新生代都采用了復制算法,因為在新生代中每次進行垃圾回收時都有大量的 垃圾對象被回收,需要復制的對象(存活的)比較少,不存在大量的對象在內存中被來回復制的問題,因此采用復制算法能安全、高效的回收新生代大量的生命周期的對象并釋放內存空間。

jvm將新生代進一步劃分為一塊大的eden區和兩塊比較小的Survivor區,Survivor區又分為SurvivorFrom和SurvivorTo區,jvm在運行過程中主要使用eden區和SurvivorFrom區,進行垃圾回收時會將eden區和SurvivorFrom區中存活的對象復制到SurvivorTo區,然后清理eden區和SurvivorFrom區。

老年代主要存放聲音周期比較長的對象和大對象,因為每次只有少量的非存活的對象被回收,因而在老年代采用標記清除算法。

在jvm中海油一個區域,即方法區中的永久代,永久代用存儲class類、常量、方法描述等,在永久代主要回收廢棄的常量和無用的類。

jvm內存中的對象組要被分配到新生代中的eden區和SurvivorFrom區,在少數情況下回直接分配到來年代,在新生代的eden區和SurvivorFrom區的內存空間不足時會觸發一次gc,該過程成為minorgc。在minorgc后,eden區和SurvivorFrom區中存活的對象會被復制到SurvivorTo中,然后eden區和SurvivorFrom區中的對象被清理,如果此時在SurvivorTo區無法找到連續的內存空間存儲某個對象,則將這個對象直接存儲到老年代,若Survivor區的對象經過一次gc之后仍然存活,則其年齡+1,默認情況下,對象在年齡達到15時,將被移動到老年代。

Java中的內存區域

JVM 的內存區域分為私有區域(程序計數器、虛擬機棧、本地方法區)、線程共享區域(對、方法區)和直接內存。
在這里插入圖片描述

線程私有區域的生命周期與線程仙童,隨線程的啟動而創建,隨著線程的結束而銷毀,在JVM內部,每個線程斗魚操作系統的本地線程直接映射,因此線程私有區域的存在與否和本地線程的啟動和銷毀對應

線程共享區域隨著虛擬機的餓啟動而創建,隨著關閉而銷毀

直接內存也交所對外內存,他并不是JVM運行時的數據區的一部分,但是在并發中被頻繁使用,JDK的nio模塊提供基于CHannel和Buffer的IO操作就是基于對堆外內存實現的,nio模塊通過調用本地方法庫直接在操作系統上分配堆內存,然后直接使用DirectByteBuffer對象作為這塊內存的引用對內存進行操作,Java進程可以通過對外內存技術避免在Java對和Native中來回復制數據帶來的資源蘭妃和性能損耗和性能消耗,因此對外內存在搞并發場景下被廣泛使用。

程序計數器

程序計數器 線程私有
去內存泄漏的問題,它是一塊很小的內存空間,用于存儲當前運行的線程鎖執行的字節碼的行號指示器,每個運行中的線程中都有一個獨立的程序計數器,在方法正在執行時,該方法的程序計數器記錄的是事實虛擬機字節碼指令的地址,如果該方法執行的是本地方法,則程序計數器的值為空

虛擬機棧

線程私有 描述Java方法的執行過程,虛擬機棧是描述Java方法的執行過程的內存模型,提前在當前棧針中存儲了局部變量表、動態鏈接、方法出口等信息。同時,棧針用來存儲部分運行時數據及其數據結構,處理動態鏈接方法的返回值和異常分派。

棧針用來記錄方法的執行過程,在方法被執行時虛擬機會為其創建一個與之對應的棧針,方法的執行和返回對應棧針在虛擬機棧中的入棧和出棧,無論方法時正常運行還是異常完成(拋出了在方法內未被捕獲的異常),都是為方法運行結束。

本地方法區

本地方法區和虛擬機棧作用類似,區別是虛擬機棧為執行Java方法服務,本地方法棧為本地方法服務

堆,也叫云信使數據區,線程共享。在JVM運行過程中創建的對象和產生的數據都被存儲在堆中,堆是被線程共享的內存區域,也是垃圾回收器進行垃圾回收的最重要的內存區域。由于現代JVM采用分代回收算法,因此,JVM堆從GC的角度可以細分為新生代、老年代、永久代。

方法區

方法區,線程共享,方法區也成為永久代,用于存儲常量、靜態變量、類信息、即時編譯器編譯后的機器碼、運行時常量池等數據

JVM吧GC分代手機款張志方法區,及時用Java堆的永久代來實現方法區,這樣JVM的垃圾回收期就可以像管理java堆一樣管理這部分內存,永久代的內存回收主要正對常量池的回收和類的卸載,以你可回收的對象很少

常量被存儲在運行時常量池中,是方法區的一部分,靜態病例也屬于方法區的一部分,在類信息中不但保存了類的版本、字段、方法、接口等描述信息,還保存了常量信息

在即時編譯后,代碼的內容將在執行階段(類加載完成之后)被保存在付費區的運行時常量池中,Java虛擬機堆class文件每一部分的格式都有明確的規定,只有符合jvm規范的class文件才能通過虛擬機的檢查,然后被裝載、執行。

JVM的運行時區域

JVM的運行時區域也叫作jvm堆,從gc的角度講jvm分成新生代、老年代、永久代。其中新生代默認占1/3堆內存空間,老年代默認占用2/3堆內存空間,永久代占非常少的堆內存空間,新生代又分為Eden區、ServivorFrom區、ServivorTo區,Eden區默認占用8/10新生代空間,ServivorFrom區和ServivorTo區默認占用1/10新生代空間。

新生代

新生代分為 Eden區、ServivorFrom區、ServivorTo區。JVM新創建的對象(除了大對象)會被存放在新生代,默認占用新生代的1/3空間,由于jvm會頻繁創建對象,所以新生代會頻繁出發minorGC進行來及回收。

  1. Eden區 java次年創建的對象首相會被存放在Eden區,如果新創建的對象屬于大對象,,則直接分配到老年區,大對象的定義和具體的jvm版本,堆大小和垃圾回收策略有關,一般為2-128K,可通過xx:PretenureSizeThreshoud設置大小,在Eden區的內存空間不足時會觸發minorGC,對新生代進行一次垃圾回收。
  2. ServivorTo區 保留上一次minorGC時的幸存者
  3. ServivorFrom區 將上一次minorGC的幸存者作為這一次minorGC的被掃描這

新生代的gc過程叫minorGC,采用復制算法實現

  1. 把Eden區和ServivorFrom區中村活的對象復制到ServivorTo區。如果某對像的年齡達到 老年代的標準(對象晉升老念叨的標準由XX:maxTenuringThreshold設置,默認是15),將其復制到老年代,同事吧這些對象的年齡+1,如果ServivorTo區的內存空間不夠,則也直接將其復制到老年代;如果對象屬于大對象(2-128k的對象屬于大對象),則也直接將其復制到老年代
  2. 清空Eden區和ServivorFrom區中的對象
  3. 將ServivorTo區和ServivorFrom區互換,原來ServivorTo區成為下一次GC的ServivorFrom區。

老年代

老對象主要存放長生命周期的對象和大對象,老年代的gc成為majorGC,在老年代,對象比較穩定,majorGC不會被頻繁出發,在進行majorGC前,jvm會進行一次minorGC,在minorGC后人人出現老遍地當且僅當老年代空間不足或者無法找到足夠大的連續內存空間分配給新創建的大對象是,會觸發majorGC進行垃圾回收,釋放jvm的內存空間

majorGC采用標記清楚算法,該算法首先會掃描所有對象并標記存活的對象,然后揮手未被標記的對象,釋放內存空間

因為先要掃描老年代的所有對象再回收,所以majorGC的耗時比較長,majorGC的標記清楚算法容易產生內存碎片,在老年代沒還有足夠存儲空間可分配是,會拋出oom異常

永久代

永久代值內存的永久保存區域,主要存放class和meta的信息,class在類加載時被放入到永久代,永久代和老年代、新生代不同,過程不會再程序運行過程中對永久代的內存進行清理,這也導致永久代的內存會隨著加載class文件的增加而增加,在加載的class文件過多時會拋出oom異常,比如Tomcat引用jar文件過多導致jvm內存不足而無法啟動

需要注意的是,在java8中永久代已經被元數據區(元空間)取代,元數據區的作用和永久代類似,二者最大的區別在于:元數據并沒有使用虛擬機的內存,而是使用直接內存,因此元空間大小不受jvm內存的限制,主要和操作系統的內存有關

在java8中,jvm將類的元數據放在本地內存中,將常量池和類的靜態變量放入到java堆中,這樣jvm能夠加載多少元數據信息就不再有jvm的最大可用內存空間決定,而是由操作系統的實際可用內存空間決定。

面試題

雙親委派模型的好處:

·主要是為了安全性,避免用戶自己編寫的類動態替換 Java的一些核心類,比如String。
同時也避免了類的重復加載,因為JVM中區分不同類,不僅僅是根據類名,相同的class文件被不同的
ClassLoader加載就是不同的兩個類

GC如何判斷對象可以被回收

。引用計數法:每個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數為0時可以
回收,
可達性分析法:從 GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots 沒有任
何引用鏈相連時,則證明此對象是不可用的,那么虛擬機就判斷是可回收對象。
引用計數法,可能會出現A引用了B,B又引用了A,這時候就算他們都不再使用了,但因為相互引用計數器=1永遠無法被回收。

GC Roots的對象有:

1.虛擬機棧(棧幀中的本地變量表)中引用的對象
2.方法區中類靜態屬性引用的對象
3.方法區中常量引用的對象
4.本地方法棧中JNI(即一般說的Native方法)引用的對象
可達性算法中的不可達對象并不是立即死亡的,對象擁有一次自我拯救的機會。對象被系統宣告死亡至少要經歷兩次標記過程:第一次是經過可達性分析發現沒有與GC Roots相連接的引用鏈,第二次是在由虛擬機自動建立的EinalizerM列中判斷具否雪更執行finalized方法
當對象變成(GC Roots)不可達時,GC會判斷該對象是否覆蓋了finalize方法,若未覆蓋,則直接將其回收。否則,若對象未執行過finalize方法,將其放入F-Queue隊列,由一低優先級線程執行該隊列中對象的finalize方法。執行finalize方法完畢后,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“復活1
每個對象只能觸發一次finalize0方法
由于finalize0方法運行代價高昂,不確定性大,無法保證各個對象的調用順序,不推薦大家使用,建議遺忘它。

Jdk1.7到Jdk1.8 java虛擬機發生了什么變化?

1.7中存在永久代,1.8中沒有永久代,替換它的是元空間,元空間所占的內存不是在虛擬機內部,而是本地內存空間,這么做的原因是,不管是永久代還是元空間,他們都是方法區的具體實現,之所以元空間所占的內存改成本地內存,官方的說法是為了和JRockit統一,不過額外還有一些原因,比如方法區所存儲的類信息通常是比較難確定的,所以對于方法區的大小是比較難指定的,太小了容易出現方法區溢出,太大了又會占用了太多虛擬機的內存空間,而轉移到本地內存后則不會影響虛擬機所占用的內存

Java的內存結構,堆分為哪幾部分,默認年齡多大進入老年代

1.年輕代
a.Eden區(8)
b. From Survivor區 (1)c. To Survivor區(1)2.老年代
默認對象的年齡達到15后,就會進入老年代

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/34936.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/34936.shtml
英文地址,請注明出處:http://en.pswp.cn/news/34936.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Hadoop安裝完全分布式搭建

1、安裝Hadoop 上傳Hadoop的指定路徑/root/softwares 解壓安裝 cd /root/softwares && tar -zxvf hadoop-2.7.3.tar.gz -C /usr/local配置環境變量 vim /etc/profile # Hadoop Environment export HADOOP_HOME/usr/local/hadoop-2.7.3 export PATH$PATH:$HADOOP_HOM…

openCV使用c#操作攝像頭

效果如下: 1.創建一個winform的窗體項目(框架.NET Framework 4.7.2) 2.Nuget引入opencv的c#程序包(版本最好和我一致) 3.后臺代碼 using System; using System.Collections.Generic; using System.ComponentModel;…

用友-NC-Cloud遠程代碼執行漏洞[2023-HW]

用友-NC-Cloud遠程代碼執行漏洞[2023-HW] 一、漏洞介紹二、資產搜索三、漏洞復現PoC小龍POC檢測腳本: 四、修復建議 免責聲明:請勿利用文章內的相關技術從事非法測試,由于傳播、利用此文所提供的信息或者工具而造成的任何直接或者間接的后果及損失&#…

Leetcode-每日一題【劍指 Offer 24. 反轉鏈表】

題目 定義一個函數&#xff0c;輸入一個鏈表的頭節點&#xff0c;反轉該鏈表并輸出反轉后鏈表的頭節點。 示例: 輸入: 1->2->3->4->5->NULL輸出: 5->4->3->2->1->NULL 限制&#xff1a; 0 < 節點個數 < 5000 解題思路 1.題目要求我們反轉…

Windows下運行Tomcat服務時報GC Overhead Limit Exceeded

根本原因是在新建Tomcat作為Windows服務時&#xff0c;系統默認設置的堆內存太小了&#xff0c;我們打開/bin/service.bat文件&#xff0c;將如下圖所示的默認值改大一些就好了 if "%JvmMs%" "" set JvmMs512 if "%JvmMx%" "" set J…

高防cdn和高防服務器有什么不一樣?

高防cdn&#xff1a; 相信很多看過我們文章的小伙伴對cdn已經很了解了&#xff0c;cdn的原理很簡單&#xff0c;就是構建在網絡上的很多個節點&#xff0c;為網站作內容 分發。使用戶就近獲取所需資源。且分配的cdn節點都是高防節點&#xff0c;每個節點都有防御功能。還…

【考研復習】24王道數據結構課后習題代碼|第3章棧與隊列

文章目錄 3.1 棧3.2 隊列3.3 棧和隊列的應用 3.1 棧 int symmetry(linklist L,int n){char s[n/2];lnode *pL->next;int i;for(i0;i<n/2;i){s[i]p->data;pp->next;}i--;if(n%21) pp->next;while(p&&s[i]p->data){i--;pp->next;}if(i-1) return 1;…

Python flask-restful 框架講解

1、簡介 Django 和 Flask 一直都是 Python 開發 Web 的首選&#xff0c;而 Flask 的微內核更適用于現在的云原生微服務框架。但是 Flask 只是一個微型的 Web 引擎&#xff0c;所以我們需要擴展 Flask 使其發揮出更強悍的功能。 python flask框架詳解&#xff1a;https://blog.…

sentinel簡單使用

核心demo&#xff1a; 1 引入依賴: <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId><version>1.8.0</version> </dependency>2 核心代碼&#xff1a; 3 限流保護代碼&#xff1a;…

【Megatron-DeepSpeed】張量并行工具代碼mpu詳解(四):張量并行版Embedding層及交叉熵的實現及測試

相關博客 【Megatron-DeepSpeed】張量并行工具代碼mpu詳解(四)&#xff1a;張量并行版Embedding層及交叉熵的實現及測試 【Megatron-DeepSpeed】張量并行工具代碼mpu詳解(三)&#xff1a;張量并行層的實現及測試 【Megatron-DeepSpeed】張量并行工具代碼mpu詳解(一)&#xff1a…

【HarmonyOS】@ohos.request 上傳下載的那些事兒

【關鍵字】 ohos.request、上傳下載? 【寫在前面】 在進行HarmonyOS應用開發時&#xff0c;可能需要進行上傳或下載文件功能開發&#xff0c;本文章主要進行上傳下載相關功能介紹和一些注意事項及FAQ。 【上傳開發步驟】 步驟1&#xff1a;上傳下載接口需要申請ohos.permis…

GitOps 與 DevOps:了解關鍵差異,為企業做出最佳選擇

在軟件開發領域&#xff0c;GitOps 和 DevOps 是加強協作和實現軟件交付流程自動化的重要技術。雖然這兩種模式都旨在提高軟件開發生命周期的效率&#xff0c;但它們的核心原則和實施方式卻各不相同。 本篇文章將幫助您了解 GitOps 和 DevOps 之間的差異、它們的工作流程&am…

新知識:Monkey 改進版之 App Crawler

原生Monkey 大家知道Monkey是Android平臺上進行壓力穩定性測試的工具&#xff0c;通過Monkey可以模擬用戶觸摸屏幕、滑動、按鍵等偽隨機用戶事件來對設備上的程序進行壓力測試。而原生的Android Monkey存在一些缺陷&#xff1a; 事件太過于隨機&#xff0c;測試有效性大打折扣…

【2023新教程】樹莓派4B開機啟動-樹莓派第一次啟動-樹莓派不使用顯示器啟動-樹莓派從購買到啟動一步一步完全版!

背景 閑來無事&#xff0c;在咸魚上買了一個樹莓派4B。買來配件都十分齊全&#xff0c;于是就想著啟動來測試一下。下面是樹莓派無顯示器第一次啟動的全過程&#xff0c;包含安裝系統。 網上的教程大多需要額外使用顯示器、鼠標、鍵盤之類的外設。然而&#xff0c;樹莓派本身就…

從一到無窮大 #10 討論 Apache IoTDB 大綜述中看到的優勢和不足點

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。 本作品 (李兆龍 博文, 由 李兆龍 創作)&#xff0c;由 李兆龍 確認&#xff0c;轉載請注明版權。 文章目錄 引言問題定義新技術數據模型schemalessTsfile設計雙MemTable高級可擴展查詢其他 IotD…

免費開源的多種人工智能項目,比如:訓練一個模型,讓人工智能玩王者榮耀

免費開源的多種人工智能項目&#xff0c;比如&#xff1a;訓練一個模型&#xff0c;讓人工智能玩王者榮耀。 全文大綱 PULSE - 該開源項目可以通過給圖片增加像素點來實現去馬賽克或高清化。 Depix - 給打了馬賽克的文字去碼。 TecoGAN - 給視頻去馬賽克或者進行超分辨率。 Sk…

計算機網絡-專業術語

計算機網絡-專業術語 實體 實體:任何可發送或接收信息的硬件或軟件進程 對等實體:收發雙方相同層次中的實體 協議 控制兩個對等實體進行邏輯通信的規則的集合 協議三要素 語法 定義所交換的信息的格式 是用戶數據與控制信息的結構和格式 語義 定義收發雙方所需要完成的操作…

go 切換代理

常用 $ go env -w GO111MODULEon $ go env -w GOPROXYhttps://goproxy.cn,direct 切換成阿里云 go env -w GOPROXYhttps://mirrors.aliyun.com/goproxy/,direct 很多需要切換到阿里云才行 刪除 $ go env -u GOPROXY 查看 $ go env 或者 go env list go get 出錯的時候 …

Kotlin 基礎教程一

Kotlin 基本數據類型 Java | Kotlin byte Byte short Short int Int long Long float Float double Double boolean Boolean c…

一 、個性化電商廣告推薦系統介紹

一 個性化電商廣告推薦系統介紹 1.1 數據集介紹 Ali_Display_Ad_Click是阿里巴巴提供的一個淘寶展示廣告點擊率預估數據集 數據集來源&#xff1a;天池競賽 原始樣本骨架raw_sample 淘寶網站中隨機抽樣了114萬用戶8天內的廣告展示/點擊日志&#xff08;2600萬條記錄&#xff…