首頁>技術>

1 JVM記憶體分哪幾個區,每個區的作用是什麼?

java虛擬機器主要分為以下幾個區:

(1)方法區

a. JDK7中稱為永久代,JDK8及之後稱為元空間,在該區內很少發生垃圾回收,但是並不代表不發生GC,在這裡進行的GC主要是對方法區裡的常量池和對型別的解除安裝b. 方法區主要用來儲存已被虛擬機器載入的類的資訊、常量、靜態變數和即時編譯器編譯後的程式碼等資料。c. 該區域是被執行緒共享的。d. 方法區裡有一個執行時常量池,用於存放靜態編譯產生的字面量和符號引用。該常量池具有動態性,也就是說常量並不一定是編譯時確定,執行時生成的常量也會存在這個常量池中。

(2)虛擬機器棧:

a. 虛擬機器棧也就是我們平常所稱的棧記憶體,它為java方法服務,每個方法在執行的時候都會建立一個棧幀,用於儲存區域性變量表、運算元棧、動態連結和方法出口等資訊。b. 虛擬機器棧是執行緒私有的,它的生命週期與執行緒相同。c. 區域性變量表裡儲存的是基本資料型別、returnAddress型別(指向一條位元組碼指令的地址)和物件引用,這個物件引用有可能是指向物件起始地址的一個指標,也有可能是代表物件的控制代碼或者與物件相關聯的位置。區域性變數所需的記憶體空間在編譯器間確定d. 運算元棧的作用主要用來儲存運算結果以及運算的運算元,它不同於區域性變量表透過索引來訪問,而是壓棧和出棧的方式e. 每個棧幀都包含一個指向執行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法呼叫過程中的動態連線.動態連結就是將常量池中的符號引用在執行期轉化為直接引用。

(3)本地方法棧:本地方法棧和虛擬機器棧類似,只不過本地方法棧為Native方法服務。

(4)堆

java堆是所有執行緒所共享的一塊記憶體,在虛擬機器啟動時建立,幾乎所有的物件例項都在這裡建立,因此該區域經常發生垃圾回收操作。

(5)程式計數器:

記憶體空間小,位元組碼直譯器工作時透過改變這個計數值可以選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理和執行緒恢復等功能都需要依賴這個計數器完成。該記憶體區域是唯一一個java虛擬機器規範沒有規定任何OOM情況的區域。

2 heap 和stack 有什麼區別

(1)申請方式

stack:由系統自動分配。例如,宣告在函式中一個區域性變數 int b; 系統自動在棧中為 b 開闢空間

heap:需要程式設計師自己申請,並指明大小,在 c 中 malloc 函式,對於Java 需要手動 new Object()的形式開闢

(2)申請後系統的響應

stack:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢位。

(3)申請大小的限制

stack:棧是向低地址擴充套件的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS 下,棧的大小是 2M(預設值也取決於虛擬記憶體的大小),如果申請的空間超過棧的剩餘空間時,將提示 overflow。因此,能從棧獲得的空間較小。

heap:堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。這是由於系統是用連結串列來儲存的空閒記憶體地址的, 自然是不連續的,而連結串列的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬記憶體。由此可見, 堆獲得的空間比較靈活,也比較大。

(4)申請效率的比較

stack:由系統自動分配,速度較快。但程式設計師是無法控制的。

heap:由 new 分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便。

(5)heap和stack中的儲存內容

stack:在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址, 然後是函式的各個引數,在大多數的 C 編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。

當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續執行。

heap:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容由程式設計師安排。

3 java類載入過程?

Java類載入需要經歷一下幾個過程:

(1)載入

載入時類載入的第一個過程,在這個階段,將完成一下三件事情:

a. 透過一個類的全限定名獲取該類的二進位制流。b. 將該二進位制流中的靜態儲存結構轉化為方法去執行時資料結構。 c. 在記憶體中生成該類的Class物件,作為該類的資料訪問入口。

(2)連結

2.1)驗證

驗證的目的是為了確保Class檔案的位元組流中的資訊不會危害到虛擬機器.在該階段主要完成以下四鍾驗證:

a. 檔案格式驗證:驗證位元組流是否符合Class檔案的規範,如主次版本號是否在當前虛擬機器範圍內,常量池中的常量是否有不被支援的型別.b. 元資料驗證:對位元組碼描述的資訊進行語義分析,如這個類是否有父類,是否繼承了不被繼承的類等。c. 位元組碼驗證:是整個驗證過程中最複雜的一個階段,透過驗證資料流和控制流的分析,確定程式語義是否正確,主要針對方法體的驗證。如:方法中的型別轉換是否正確,跳轉指令是否正確等。d. 符號引用驗證:這個動作在後面的解析過程中發生,主要是為了確保解析動作能正確執行。

2.2)準備

準備階段是為類的靜態變數分配記憶體並將其初始化為預設值,這些記憶體都將在方法區中進行分配。準備階段不分配類中的例項變數的記憶體,例項變數將會在物件例項化時隨著物件一起分配在Java堆中。

2.3)解析

該階段主要完成符號引用到直接引用的轉換動作。解析動作並不一定在初始化動作完成之前,也有可能在初始化之後。

(3)初始化 初始化時類載入的最後一步,前面的類載入過程,除了在載入階段使用者應用程式可以透過自定義類載入器參與之外,其餘動作完全由虛擬機器主導和控制。到了初始化階段,才真正開始執行類中定義的Java程式程式碼。

4 什麼是類載入器,類載入器有哪些?

實現透過類的全限定名獲取該類的二進位制位元組流的程式碼塊叫做類載入器。

主要有以下四種類載入器:

(1)啟動類載入器(Bootstrap ClassLoader)用來載入java核心類庫,無法被java程式直接引用。

(2)擴充套件類載入器(extensions class loader):它用來載入 Java 的擴充套件庫。Java 虛擬機器的實現會提供一個擴充套件庫目錄。該類載入器在此目錄裡面查詢並載入 Java 類。

(3)系統類載入器(system class loader)也叫應用類載入器:它根據 Java 應用的類路徑(CLASSPATH)來載入 Java 類。一般來說,Java 應用的類都是由它來完成載入的。可以透過 ClassLoader.getSystemClassLoader()來獲取它。

(4)使用者自定義類載入器,透過繼承 java.lang.ClassLoader類的方式實現。

5 java中垃圾收集的方法有哪些?

1)引用計數法演算法 應用於:微軟的COM/ActionScrip3/Python等

a) 如果物件沒有被引用,就會被回收,缺點:需要維護一個引用計算器

2)可達性分析演算法 以根物件集合(GC Roots)為起始點,按照從上至下的方式搜尋被根物件集合所連線的目標物件是否可達。不可達的,就意味著該物件已經死亡,可以標記為垃圾物件。

3)複製演算法 年輕代中使用的是Minor GC,這種GC演算法採用的是複製演算法(Copying)

a) 效率高,缺點:需要記憶體容量大,比較耗記憶體

b) 使用在佔空間比較小、重新整理次數多的新生區

4)標記-清除演算法 老年代一般是由標記清除或者是標記清除與標記整理的混合實現

a) 效率比較低,會產生碎片。

5)標記-壓縮演算法 老年代一般是由標記清除或者是標記清除與標記整理的混合實現

a) 效率低速度慢,需要移動物件,但不會產生碎片。

6)標記-清除-壓縮演算法 標記清除-標記壓縮的集合,多次GC後才Compact

a) 適用於佔空間大重新整理次數少的養老區,是4)和5)的集合體

6 如何判斷一個物件是否存活?(或者GC物件的判定方法)

判斷一個物件是否存活有兩種方法:

(1)引用計數法

所謂引用計數法就是給每一個物件設定一個引用計數器,每當有一個地方引用這個物件時,就將計數器加一,引用失效時,計數器就減一。當一個物件的引用計數器為零時,說明此物件沒有被引用,也就是“死物件”,將會被垃圾回收.

引用計數法有一個缺陷就是無法解決迴圈引用問題,也就是說當物件A引用物件B,物件B又引用者物件A,那麼此時A,B物件的引用計數器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機器都沒有采用這種演算法。

(2)可達性演算法(引用鏈法)

該演算法的基本思路就是透過以GC Roots物件作為起點,從這些節點開始向下搜尋,搜尋走過的路徑被稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連時(即從GC Roots節點到該節點不可達),則證明該物件是不可用的。

在java中可以作為GC Roots的物件有以下幾種:虛擬機器棧中引用的物件、方法區類靜態屬性引用的物件、方法區常量池引用的物件、本地方法棧JNI引用的物件。

7 簡述java記憶體分配與回收策略以及Minor GC和Major GC(full GC)

記憶體分配:

(1)棧區:棧分為java虛擬機器棧和本地方法棧

(2)堆區:堆被所有執行緒共享區域,在虛擬機器啟動時建立,唯一目的存放物件例項。堆區是gc的主要區域,通常情況下分為兩個區塊年輕代和年老代。更細一點年輕代又分為Eden區,主要放新建立物件,From survivor 和 To survivor 儲存gc後倖存下的物件,預設情況下各自佔比 8:1:1。

(3)方法區:被所有執行緒共享區域,用於存放已被虛擬機器載入的類資訊,常量,靜態變數等資料。被Java虛擬機器描述為堆的一個邏輯部分。習慣上也叫它永久代(permanment generation)

(4)程式計數器:當前執行緒所執行的訊號指示器。透過改變計數器的值來確定下一條指令,比如迴圈,分支,跳轉,異常處理,執行緒恢復等都是依賴計數器來完成。執行緒私有的。

回收策略以及Minor GC和Major GC:

(1)物件優先在堆的Eden區分配。

(2)大物件直接進入老年代。

(3)長期存活的物件將直接進入老年代。

當Eden區沒有足夠的空間進行分配時,虛擬機器會執行一次Minor GC.Minor GC通常發生在新生代的Eden區,在這個區的物件生存期短,往往發生GC的頻率較高,回收速度比較快;Full Gc/Major GC 發生在老年代,一般情況下,觸發老年代GC的時候不會觸發Minor GC,但是透過配置,可以在Full GC之前進行一次Minor GC這樣可以加快老年代的回收速度。

7
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • python基礎語法