程序在JVM是如何執行?爲什麼要GC?垃圾回收算法

程序計數器:

程序計數器是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。
程序計數器處於線程的獨佔區。
如果線程執行的是java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址。如果執行的是native方法,這個計數器的值爲undefined(不明確的)
此區域是唯一一個在java虛擬機規範中沒有規定任何OutOfMemoryError情況的區域(內存溢出)。
每一個線程都有自己的獨立的程序計數器。

虛擬機棧:

java虛擬機棧描述的是java方法執行的動態內存模型。
棧幀:
每一個方法執行,都會創建一個棧幀,伴隨着方法從創建到執行完成。用於存儲局部變量表,操作數棧,動態鏈接,方法出口等。
在這裏插入圖片描述
局部變量表:
存放編譯期可知的各種基本數據類型,引用類型,returnAddress類型。
局部變量表的內存空間在編譯時期就會完成分配,當進入一個方法時,這個方法需要在幀分配多少內存是固定的,在方法運行期間是不會改變局部變量表的大小。
因爲我們局部變量表是存在棧中的,我們存放的不是對象,是對象的引用。這個引用的大小是不會變化的。
java虛擬機棧的大小:
棧是有大小限制,存放大量的棧幀後會有StackOverflowError棧內存溢出。
OutOfMemory如果棧內存不限制大小,程序會不停的一直遞歸調用,直至將內存放的超出jvm設定的虛擬機棧的大小,無法申請到內存,然後會拋內存溢出。
測試用例:

public class Test {
    public static void main(String[] args) {
        func();
    }
    private static void func(){
        System.out.println("正在執行》》》");
        func();
    }
}

在這裏插入圖片描述

本地方法棧:

虛擬機棧爲虛擬機執行java方法服務。
本地方法棧是虛擬機爲虛擬機執行native方法提供服務。

java堆

存放對象實例
垃圾收集器管理的主要區域
新生代,老年代,Eden空間
OutOfMemory.

JVM概述:

JVM在哪?
JVM是什麼?
在這裏插入圖片描述
站在OS角度上看待JVM。
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
關於類加載時的初始化:
1.所有的靜態屬性默認值都是0或者0的變形(null,false)
JVM在內存上分配空間時,先調用memset(這塊內存,0x0);

2.按照代碼書寫順序,進行靜態屬性初始化
—1.static int a=10;直接初始化
—2.static { a=10; }靜態代碼塊

靜態方法和普通方法的區別:

在這裏插入圖片描述
靜態方法和普通方法都是被調用時才執行。
什麼時候執行不是他們兩個的區別

真正的區別:
調用時的形參不同(隱式參數)
形參是實參的複製。
在這裏插入圖片描述

變量:

怎麼理解變量?變量就是內存上的一段空間,只是根據變量的各種特徵,來看怎麼具體解釋這段內存

類型 名稱 = 值;
在這裏插入圖片描述
類型:這段內存到底有多長:如何解釋
int 32 bit
long 64 bit
int a=666;
就代表是666的值
String s=666;
JVM會根據666區堆上找對應的對象去。

類型:兩大類:基本數據類型和引用數據類型。變量類型能決定變量在內存的哪個區域上嗎?
回答:不可以

形態:局部變量,形參,屬性,靜態屬性。
只有變量的形態才能決定變量在內存的哪一個區域上。

局部變量,形參屬於棧幀的一部分,而棧幀時在棧上的
屬性是屬於對象的一部分,而對象在堆上。
靜態屬性屬於類的一部分,而類在方法區上

class Person{
	String name;都是引用,因爲是屬性所以在堆上
}
class Main{
public static void main(String []args){
		String name;都是引用,但是局部變量,所以是在棧上的
	}
}

1.程序是如何運行起來的:

  1. 首先把需要運行的類,都過類加載器放置到內存中(方法區)中。
  2. 執行引擎執行類中的方法代碼 —執行過程中需要棧的輔助 – 中途會用到堆中的對象等數據
    棧是用來做什麼的?
    棧是用來存儲棧幀的,棧幀是描述執行到哪一個方法,棧幀中有一個重要的結構就是局部變量表(存儲局部變量和形參)。
    堆是用來做什麼的?
    堆就是進行存儲對象的
    方法區中存放的是什麼?
    方法區存放的是編譯好的字節碼,存放靜態屬性
    方法區中保存的就是指令:
    load a 讀取a的值
    load b 讀取b的值
    add * a * b 進行運算
    save r 最後賦值
    在這裏插入圖片描述
    GC垃圾回收:
    程序計數器(PC)不需要GC,線程或者就一定要用,線程死了就一定沒用了。
    :不需要GC負責。線程活的時候使用,線程消亡的時候消亡。
僞代碼:
createThread(){
	創建線程對象;
	爲這個對象分配資源;
	棧和pc內存;
	malloc(pc);
	malloc();
}
destoryThread(){
	free(pc);
	free();
}

總結:線程創建時創建,回收時回收。
方法區:類的加載和卸載過程。
需要類的時候就會加載/類肯定不需要了就去卸載。
類的佔用空間相對於對象佔用的比例要小很多,卸載的條件複雜。
GC是需要負責這裏的。
:需要GC負責
GC回收內存的單位主要就是對象 ----以對象爲單位進行垃圾回收
GC就是回收無用對象。
1,GC 是如何判斷一個對象是無用的呢?
當一個對象沒有引用指向的時候,可以認爲這個對象就是沒有用的對象。
引用計數器方案

  class JavaObject{
  int referenceCount=0;
  //對象的屬性
  //對象的monitor lock
}
S s=new S();
JVM內部s對應的JavaObject.referenceCount++;
// s出了作用域之後
JVM內部s的對象的JavaObject.referenceCount--;
當referenceCount==0,表示該對象沒有被引用,看做是沒有用的對象。

引用計數方案會出現出的問題:循環引用
在這裏插入圖片描述
Java JVM比較先進,是不使用引用計數的方法。
下面分析可達性分析:
application 活着的過程,有一些引用(變量)是必須活着的
1.所有線程的棧幀中的局部變量(其中的引用)是必須活着的。
2.所有靜態屬性(其中的引用)是必須活着的。
這一系列引用被稱爲GC Roots
在這裏插入圖片描述
在這裏插入圖片描述
如何判斷對象是否還活着:
1.看當前對象是否被引用指向-------引用計數法—JVM不使用,存在循環引用問題
2.通過GC Roots 名單 一路向下查找-------可達性分析 就是分析的過程耗時會有點長,因爲需要遍歷整顆樹

(垃圾回收算法)對象的清理過程 釋放內存的過程

在這裏插入圖片描述
既然沒有一個通用的算法幫助我們做到快速,並且減少內存碎片。
呢我們想要追求,就可以進行垃圾分類。(和日常生活是一樣的道理)

垃圾分代:

在這裏插入圖片描述
Stop The World 時候,我們需要停止應用,因爲如果此時不停止,通過可達性分析,又分析出來的數據,就會被誤殺掉,所以需要停止。
JVM:
1,程序是在JVM中如何執行的
ClassLoader
內存各個區域的作用
執行引擎的大概流程
2.GC
爲什麼要GC
GC 的單位是什麼—對象
怎麼判斷一個對象活着:引用計數,可達性分析(JVM使用)
對象的漫遊路徑:
Eden ->Survior -> Old
在Young中是使用的複製算法
在Old中使用的是整理算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章