開平 做一網(wǎng)站南京seo整站優(yōu)化技術(shù)
目錄
1.JVM內(nèi)存劃分
2.JVM類加載過程
3.JVM垃圾回收機制GC
3.1.判斷誰是垃圾
3.2.如何釋放對應(yīng)的內(nèi)存
1.JVM內(nèi)存劃分
在一個Java程序運行起來之后,jvm就會從操作系統(tǒng)中申請一塊內(nèi)存,然后就會將該內(nèi)存劃分成多個部分,用于不同的用途。
(1)主要劃分成五個部分
堆、棧、元數(shù)據(jù)區(qū)(方法區(qū))、常數(shù)計數(shù)器(很少涉及)
圖示:
像堆區(qū)中的各個部分劃分,在后面的垃圾回收機制再拿出來鞭策一遍,在這里就不先做任何的贅述。
(2)每塊內(nèi)存區(qū)的功能
堆區(qū):
整個內(nèi)存區(qū)域中最大的區(qū)域,用于存放java代碼中new出來的對象、和成員變量。
棧:
一般java是使用jvm虛擬機棧,這里保存了方法的調(diào)用關(guān)系、局部變量等。也就是每個方法怎么被調(diào)用,被誰調(diào)用等
元數(shù)據(jù)區(qū):
元數(shù)據(jù)區(qū),以前也成為方法區(qū),用來存放類對象、類屬性(靜態(tài)成員)、常量
程序計數(shù)器:
屬于內(nèi)存中最小的區(qū)域,用來保存要執(zhí)行的下一條指令的地址
(3)實戰(zhàn)劃分內(nèi)存
看下嘛的一段代碼,查看下面的變量和對象分別處于哪一塊內(nèi)存中
class Demo2 {}
class Demo1 {public int a;Demo2 b = new Demo2();public String c = "love";public static int d;}public class Test {public static void main(String[] args) {Demo1 e = new Demo1();}
}
對于變量:a,b,c,d,e 其中a,b,c都屬于成員變量,存在于堆上;而e屬于局部遍歷,存在棧上;而d屬于靜態(tài)成員變量,屬于類屬性,存在于元數(shù)據(jù)區(qū)。它們只是屬于變量,里面存有一個值,會指向另一塊內(nèi)存空間。
各對象內(nèi)存分布和指向關(guān)系
以上就是對應(yīng)的各種關(guān)系。
對于上述的四個區(qū)域,堆和元數(shù)據(jù)區(qū),在整個進程中只有一份,而棧和程序計數(shù)器,是每個線程都有一份。
2.JVM類加載過程
一個.java文件變成.class文件的過程,也是從硬盤加載到內(nèi)存中,得到類對象的過程。
(1)類加載的五個環(huán)節(jié)
加載、驗證、準備、解析、初始化
(2)每個環(huán)節(jié)對應(yīng)的作用
1.加載
在硬盤上找到對應(yīng)的.class文件,并且讀取.class文件內(nèi)容
2.驗證
檢查.class文件中的內(nèi)容,是否符合要求(如文件格式等)
3.準備
給類對象分配內(nèi)存空間
4.解析
對字符串常量初始化,把剛才.class文件中的常量內(nèi)容取出來放到元數(shù)據(jù)區(qū)
5.初始化
針對類對象進行初始化,給靜態(tài)成員初始化,也就是執(zhí)行靜態(tài)代碼塊
(3)“雙親委派模型”
在第一步的加載環(huán)節(jié),目的是打開.class文件,前提就是需要通過“全限定類名”找到文件才能進行打開,所以“雙親委派模型”就是尋找.class文件的一種機制。
在這個環(huán)節(jié),涉及到一個概念:類加載器
在JVM中也會包含一些類,負責(zé)完成后續(xù)的類加載工作。其中JVM內(nèi)置了三個類加載器,負責(zé)加載不同的類
分別是三個類:BootstrapClassLoader、ExtentionClassLoader、ApplicationClassLoader
1)三個類加載器作用
所以成為雙親委派模型,但是跟準確的說法為:父親委派模型或者單親委派模型
2)加載過程
那么雙親委派模型是如何找類的呢?我們舉一個例子,假設(shè)我們自己寫了一個java程序,會給定一個全限定類名
3)雙親委派模型應(yīng)對的場景
如果自己的代碼中寫的類的名字和標準庫/擴展庫沖突了,JVM會確保加載的類是標準庫的類(不會加載自己寫的類),如果標準庫中的類無法加載,那么Java進行就沒有辦法正常工作了。
這樣還有一個好處,就是可以確保自己寫的類肯定可以被加載到。
3.JVM垃圾回收機制GC
對于Java,回收垃圾采取的是自動回收策略,策略也稱為GC。
對于GC來說,回收的其實是堆上的內(nèi)存。而對于堆,保存的主要是對象,換句話說,也就是主要回收對象,那怎么回收對象呢?主要有兩個步驟:判斷誰是垃圾和如何釋放其對應(yīng)的內(nèi)存。
3.1.判斷誰是垃圾
在判斷誰是垃圾這一步,Java是采取很保守的做法,也就是可以保證只會釋放后續(xù)不會再使用的對象,后續(xù)仍會使用到的對象,是不會進行回收的,所以才用的策略是:判斷某個對象是否存在引用指向,如果沒有引用指向,就可以判斷為垃圾,反之不行。
判斷誰是垃圾,GC有兩種策略:引用技術(shù)和可達性分析,而JVM采取的策略只有可達性分析,引用計數(shù)則不是。
(1)引用計數(shù)
1)策略:每創(chuàng)建一個對象,就在對象前面多開辟一塊空間,用來計數(shù)使用,有一個引用指向該對象,計數(shù)變量就+1,如果計數(shù)器為0,則需要回收該對象。
2)代碼舉例
class TT {}
public class GC {public static void main(String[] args) {TT a = new TT();TT b = a;}
}
對于實例化的TT對象,當(dāng)前有兩個對象指向它,所以計數(shù)值為2(分別是引用a和b),如果a=null或者b=null,則計數(shù)值-1,兩個都置為null,則計數(shù)為0.
對于引用計數(shù)存在兩個問題
問題一:會消耗額外的內(nèi)存空間
如果對象本身的內(nèi)存比較大,相比來說計數(shù)的空間就很小;但是如果對象內(nèi)存空間很小,那么計數(shù)空間就會顯得很大,就會浪費很大的空間
問題二:存在“循環(huán)引用”問題
這類問題就會讓外部代碼無法對 對象進行釋放
代碼舉例:
class T {T t;
}
public class GC {public static void main(String[] args) {T a = new T();T b = new T();a.t = b;b.t = a;}
}
這是一段有問題的代碼
圖示分析:
存在的問題:當(dāng)將引用a和b置為null時,按理來說這兩個對象是要被回收了,但是這里卻不會,因為計數(shù)不為0,不能回收。所以這就是引用計數(shù)最大的一個問題。
(2)可達性分析
JVM采取的是可達性分析,既解決了空間問題,也解決了循環(huán)引用問題。
1)定義:JVM會把對象之間的引用關(guān)系,定義成樹形結(jié)構(gòu),JVM可以不停的從根節(jié)點開始遍歷,可以訪問到的對象,成為“可達”,剩下的就是“不可達”。
標記為不可達的對象,也就被標記成垃圾。
2)舉例
例如這樣的結(jié)構(gòu),如果將c=null,那么c后面的兩個對象f和g就變成不可達,就要成為垃圾。
訪問這棵樹的所有節(jié)點,都是要通過根節(jié)點a開始。
3)確定根節(jié)點
對于根節(jié)點,可能的情況有:棧上的引用局部變量、常量池中引用的對象、方法區(qū)中的靜態(tài)成員。
3.2.如何釋放對應(yīng)的內(nèi)存
當(dāng)已經(jīng)標記好垃圾之后,該怎么回收呢?下面介紹。一共四種,前面三種是鋪墊,最后一種才是JVM真正使用的
(1)標記-清除
策略:直接把標記為垃圾對象的對應(yīng)的內(nèi)存回收,已經(jīng)回收的內(nèi)存,其他對象可以使用
缺點:這樣做很存在內(nèi)存碎片問題,后續(xù)就很難申請到一大塊連續(xù)的空間
(2)復(fù)制算法
策略:要刪除空間1中abcd中的ac,就直接將bd復(fù)制到另一塊空間中
缺點:嚴重浪費空間。比如有8G內(nèi)存,就只能使用4G
(3)標記-整理
策略:把不需要釋放的內(nèi)存空間覆蓋到需要釋放的空間上??梢越鉀Q內(nèi)存碎片問題
缺點:時間開銷很大
(4)分代回收
策略:根據(jù)不同的場景,采取不同的回收策略,這里的回收策略也就是上述介紹到的。
如何根據(jù)場景呢?就是要根據(jù)時間來,哪個時間呢?就是對象的年齡。
因為GC主要回收的地方就是堆區(qū),所以堆區(qū)上會有對應(yīng)的分區(qū),不同的區(qū)代表對象的年齡
Eden:稱為伊甸區(qū),新創(chuàng)建的對象都處于這里。
這里的對象生命周期都很短,一般經(jīng)過一輪GC就會成為垃圾。從伊甸區(qū)到達生存區(qū),采取的是復(fù)制算法
s0:稱為生存區(qū),一般經(jīng)過一輪還未被回收,就會到達下一個階段
從生存區(qū)到達幸存區(qū),也是通過復(fù)制算法
s1:稱為幸存區(qū)
這里也稱為生存區(qū),到達這里也需要通過復(fù)制算法。
Old區(qū):到達這里的對象都會成為老年代的對象,GC的掃描頻率會大幅度降低。
對于老年代中的垃圾,就會通過標記-整理的方法進行回收
總結(jié)一下:伊甸區(qū)、生存區(qū)、幸存區(qū)都是通過復(fù)制算法回收垃圾很搬運到下一個區(qū),對于老年區(qū),則是通過標記-整理回收垃圾。