JVM總結(jié)

JVM總結(jié)

JVM的位置

JVM在操作系統(tǒng)之上淤齐,它與硬件沒(méi)有直接的交互活玲!

JVM體系結(jié)構(gòu)圖

棧中絕不會(huì)有垃圾涣狗,否則會(huì)影響出棧,程序就死了舒憾!

類(lèi)加載器ClassLoader

一個(gè)類(lèi)加載到JVM的一個(gè)基本結(jié)構(gòu):

在如下幾種情況下镀钓,java虛擬機(jī)將結(jié)束生命周期:

  1. 執(zhí)行了System.exit()方法
  2. 程序正常執(zhí)行結(jié)束
  3. 程序執(zhí)行過(guò)程中遇到異常或者錯(cuò)誤而終止
  4. 由于操作系統(tǒng)出現(xiàn)錯(cuò)誤而導(dǎo)致java虛擬機(jī)終止

類(lèi)的加載镀迂、連接與初始化

在Java代碼中丁溅,類(lèi)的加載、連接與初始化過(guò)程都是在程序運(yùn)行期間完成的探遵。

  • 加載:查找并加載類(lèi)的二進(jìn)制數(shù)據(jù)

  • 連接:

    • 驗(yàn)證:確保被加載的類(lèi)的正確性
    • 準(zhǔn)備:為類(lèi)的靜態(tài)變量分配內(nèi)存骡苞,并將其初始化為默認(rèn)值
    • 解析:把類(lèi)中的符號(hào)引用轉(zhuǎn)換為直接引用

    在編譯的時(shí)候一個(gè)每個(gè)java類(lèi)都會(huì)被編譯成一個(gè)class文件吗铐,但在編譯的時(shí)候虛擬機(jī)并不知道所引用類(lèi)的地址,所以就用符號(hào)引用來(lái)代替,而在這個(gè)解析階段就是為了把這個(gè)符號(hào)引用轉(zhuǎn)化成為真正的地址的階段档悠。

  • 初始化:為類(lèi)的靜態(tài)變量賦予正確的初始值

通過(guò)代碼來(lái)理解:

class Test{
  public static int a = 1;
}
//我們程序中給定的是 public static int a = 1; //但是在加載過(guò)程中的步驟如下:
/*
1. 加載階段
編譯文件為 .class文件懂扼,然后通過(guò)類(lèi)加載烁落,加載到JVM
2. 連接階段
第一步(驗(yàn)證):確保Class類(lèi)文件沒(méi)問(wèn)題 
第二步(準(zhǔn)備):先初始化為 a=0熄云。(因?yàn)槟鉯nt類(lèi)型的初始值為0) 
第三步(解析):將引用轉(zhuǎn)換為直接引用
3. 初始化階段: 通過(guò)此解析階段,把1賦值為變量a
*/

類(lèi)的加載

類(lèi)的加載指的是將類(lèi)的.class文件中二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中诉稍,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)內(nèi)的方法區(qū)內(nèi)蝠嘉,然后再內(nèi)存中創(chuàng)建一個(gè)java.lang.Class對(duì)象用來(lái)封裝類(lèi)在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。

//對(duì)于靜態(tài)字段來(lái)說(shuō)杯巨,只有直接定義了該字段的類(lèi)才會(huì)被初始化; 
//當(dāng)一個(gè)類(lèi)在初始化時(shí)蚤告,要求其父類(lèi)全部都已經(jīng)初始化完畢了; 
//所有Java虛擬機(jī)實(shí)現(xiàn)必須在每個(gè)類(lèi)或者接口被Java程序“首次主動(dòng)使用”時(shí)才初始化他們 
public class MyTest1 {
    public static void main (String[] args){
        System.out.println(MyChild1.str2);
    } 
}
class MyParent1{
    public static String str = "hello world";
    static {
        System.out.println("MyParent1 static block ");
    }
}
class MyChild1 extends MyParent1{
    public static String str2 = "welcome";
    static{
        System.out.println("MyChild1 static block ");
    }
}
// 輸出結(jié)果:
MyParent1 static block 
MyChild1 static block 
welcome

查看類(lèi)的加載信息,并打印出來(lái):

jvm 參數(shù)介紹: 
    -XX:+TraceClassLoading,用于追蹤類(lèi)的加載信息并打印出來(lái)服爷。
所有的參數(shù)都是:
  -XX:+<option> 杜恰, 表示開(kāi)啟option選項(xiàng)
  -XX:-<option> 获诈, 表示關(guān)閉option選項(xiàng) 
  -XX:+<option>=<value> 表示將option選項(xiàng)的值設(shè)置為value

常量池概念

問(wèn)題1

public class Test2 {
    public static void main(String[] args) {
        System.out.println(Parent2.str);
    }
}

class Parent2 {
    public static final String str = "hello world";

    static {
        System.out.println("parent2 靜態(tài)代碼塊");//不會(huì)輸出
    }
}

只會(huì)輸出helloword,因?yàn)槌A吭诰幾g階段會(huì)存入到調(diào)用這個(gè)常量的方法所在的類(lèi)的常量池中心褐。本質(zhì)上舔涎,調(diào)用類(lèi)并沒(méi)有直接用到定義常量的類(lèi),因此并不會(huì)觸發(fā)定義常量的類(lèi)的初始化逗爹。

簡(jiǎn)而言之亡嫌,這里將常量存放到了Test2的常量池中,之后Test2與Parent2就沒(méi)有任何關(guān)系了掘而。但如果使用new Parent2()來(lái)調(diào)用挟冠,必將初始化Parent2,同時(shí)打印代碼塊里的內(nèi)容袍睡。

問(wèn)題2

public class Test3 {
    public static void main(String[] args){
        System.out.println(Parent3.str);
    }
}
class Parent3{
    public static final String str = UUID.randomUUID().toString();
    static {
        System.out.println("parent3 靜態(tài)代碼塊");//會(huì)輸出
    } 
}

答案是會(huì)輸出靜態(tài)代碼塊里的內(nèi)容知染。和問(wèn)題1案例不同的是,此常量的值調(diào)用其他方法斑胜,只要初始化運(yùn)行的時(shí)候才會(huì)被確定控淡。而問(wèn)題1的常量的值在編譯時(shí)就確定了。

ClassLoader分類(lèi)

有兩種類(lèi)型的類(lèi)加載器

  1. Java虛擬機(jī)自帶的加載器
    • 根類(lèi)加載器(BootStrap or BootClassLoader) sun.boot.class.path(加載系統(tǒng)的包伪窖,包含jdk核 心庫(kù)內(nèi)類(lèi))
    • 擴(kuò)展類(lèi)加載器(Extension or ExtClassLoader) java.ext.dirs(加載擴(kuò)展jar包中的類(lèi))
    • 系統(tǒng)(應(yīng)用)類(lèi)加載器(System or AppClassLoader) java.class.path(加載你編寫(xiě)的類(lèi)逸寓,編譯后的類(lèi))
  2. 用戶(hù)自定義的類(lèi)加載器
    • Java.long.ClassLoader的子類(lèi)(繼承)居兆,用戶(hù)可以定制類(lèi)的加載方式

代碼測(cè)試:

public class ClassLoaderDemo01 {
    public static void main(String[] args) {
        Object object = new Object();
        ClassLoaderDemo01 demo01 = new ClassLoaderDemo01();
        System.out.println(object.getClass().getClassLoader());
        System.out.println(demo01.getClass().getClassLoader());
        System.out.println(demo01.getClass().getClassLoader().getParent());
 System.out.println(demo01.getClass().getClassLoader().getParent().getParent
());
        /*
        結(jié)果:
            null
            sun.misc.Launcher$AppClassLoader@18b4aac2
            sun.misc.Launcher$ExtClassLoader@1b6d3586
            null
                **/
            } 
}

雙親委派機(jī)制

雙親委派機(jī)制的工作原理:一層一層的 讓父類(lèi)去加載覆山,最頂層父類(lèi)不能加載往下數(shù),依次類(lèi)推泥栖。

  1. 類(lèi)加載器收到類(lèi)加載的請(qǐng)求;
  2. 把這個(gè)請(qǐng)求委托給父加載器去完成簇宽,一直向上委托,直到啟動(dòng)類(lèi)加載器
  3. 啟動(dòng)器加載器檢查能不能加載(使用findClass()方法)吧享,能就加載(結(jié)束);否則拋出異常魏割,通知子加載器進(jìn)行加載
  4. 重復(fù)步驟三;

代碼測(cè)試:

package java.lang;
public class String {
    public static void main(String[] args) {
        System.out.println(1);
    }
}

大家所熟知的String類(lèi),直接告訴大家钢颂,String默認(rèn)情況下是啟動(dòng)類(lèi)加載器進(jìn)行加載的钞它。假設(shè)我也自定義一個(gè)String 。現(xiàn)在你會(huì)發(fā)現(xiàn)自定義的String可以正常編譯殊鞭,但是永遠(yuǎn)無(wú)法被加載運(yùn)行遭垛。這是因?yàn)樯暾?qǐng)自定義String加載時(shí),總是啟動(dòng)類(lèi)加載器操灿,而不是自定義加載器锯仪,也不會(huì)是其他的加載器。雙親委派機(jī)制可以確保Java核心類(lèi)庫(kù)所提供的類(lèi)趾盐,不會(huì)被自定義的類(lèi)所替代庶喜。

Native方法

凡是帶了native關(guān)鍵字的小腊,說(shuō)明java的作用范圍達(dá)不到,去調(diào)用底層C語(yǔ)言的庫(kù)!

JNI:Java Native Interface (Java本地方法接口)

凡是帶了native關(guān)鍵字的方法就會(huì)進(jìn)入本地方法棧--->Native Method Stack

作用:融合不同的編程語(yǔ)言為Java所用久窟,它的初衷是融合C/C++程序秩冈,調(diào)用C、C++的程序斥扛,于是就在內(nèi)存中專(zhuān)門(mén)開(kāi)辟了一塊區(qū)域處理標(biāo)記為native的代碼漩仙,它的具體做法是在Native Method Stack 中登記native方法,在(Execution Engine)執(zhí)行引擎執(zhí)行的時(shí)候加載Native Libraies犹赖。

程序計(jì)數(shù)器

每個(gè)線程都有一個(gè)程序計(jì)數(shù)器队他,是線程私有的。
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間峻村,它的作用可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器麸折。在虛擬機(jī)的概念模型里字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令,分支粘昨、循環(huán)垢啼、跳轉(zhuǎn)、異常處理张肾、線程恢復(fù)等基礎(chǔ)功能都需要依賴(lài)這個(gè)計(jì)數(shù)器來(lái)完成芭析。保證程序執(zhí)行順序。是一個(gè)非常小的內(nèi)存空間吞瞪,幾乎可以忽略不計(jì)馁启。

代碼案例:

public class Calc {
    public int calc(){
        int a = 100;
            int b = 200;
            int c = 300;
            return ( a + b ) * c;
        }
}

反編譯: Javap -c xx.class

  • ldc:表示將int、float或是String類(lèi)型的常量值從常量池中推送至棧頂
  • bipush:表示將單字節(jié)(-128~127)的常量值推送至棧頂芍秆。
  • sipush:表示將短整型(-32767~32768)的常量值推送至棧頂惯疙。
  • istore_1:將一個(gè)數(shù)值從操作數(shù)棧存儲(chǔ)到局部變量表
  • iadd:加
  • imul:乘

圖中使用紅框框起來(lái)的就是字節(jié)碼指令的偏移地址,偏移地址對(duì)應(yīng)的bipush 等等是jvm中的操作指令, 這是入棧指令妖啥。 當(dāng)執(zhí)行到方法calc()時(shí)在當(dāng)前的線程中會(huì)創(chuàng)建相應(yīng)的程序計(jì)數(shù)器霉颠,在計(jì)數(shù)器中為存放執(zhí) 行地址 (紅框中的)0 2 3...等等

方法區(qū)

靜態(tài)變量、常量荆虱、類(lèi)信息(構(gòu)造方法——蒿偎、接口定義)、運(yùn)行時(shí)的常量池存在方法區(qū)中怀读,但是實(shí)例變量存在堆內(nèi)存中诉位,和方法無(wú)關(guān)。(static,final,Class,常量池)

什么是棧

  1. 棧也叫棧內(nèi)存愿吹,主管Java程序的運(yùn)行不从,是在線程創(chuàng)建時(shí)創(chuàng)建,它的生命期是跟隨線程的生命期犁跪,線程 結(jié)束棧內(nèi)存也就釋放椿息。
  2. 對(duì)于棧來(lái)說(shuō)不存在垃圾回收問(wèn)題歹袁,只要線程一旦結(jié)束,該棧就Over寝优,生命周期和線程一致条舔,是線程私有的。

棧的運(yùn)行原理

Java棧的組成元素是棧幀乏矾,棧幀是一種用于幫助虛擬機(jī)執(zhí)行方法調(diào)用與方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)孟抗。他是獨(dú)立于線程的,一個(gè)線程有自己的一個(gè)棧幀钻心。封裝了方法的局部變量表凄硼、動(dòng)態(tài)鏈接信息、方法的返回地址以及操作數(shù)棧等信息捷沸。
第一個(gè)方法從調(diào)用開(kāi)始到執(zhí)行完成摊沉,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程。

Java堆是java虛擬機(jī)所管理內(nèi)存中最大的一塊內(nèi)存空間痒给,處于物理上不連續(xù)的內(nèi)存空間说墨,只要邏輯連續(xù)即可,主要用于存放各種類(lèi)的實(shí)例對(duì)象苍柏。該區(qū)域被所有線程共享尼斧,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,用來(lái)存放對(duì)象的實(shí)例试吁,幾乎所有的對(duì)象以及數(shù)組都在這里分配內(nèi)存(棧上分配棺棵、標(biāo)量替換優(yōu)化技術(shù)的例外)。
在 Java 中潘悼,堆被劃分成兩個(gè)不同的區(qū)域:新生代 ( Young )律秃、老年代 ( Old )爬橡。新生代 ( Young ) 又被劃分為三個(gè)區(qū)域:Eden治唤、From Survivor(S0)To Survivor(S1)糙申。如圖所示:

新生代

新生代是類(lèi)誕生宾添,成長(zhǎng),消亡的區(qū)域柜裸,一個(gè)類(lèi)在這里產(chǎn)生缕陕,應(yīng)用,最后被垃圾回收器收集疙挺,結(jié)束生命扛邑。

新生代含有兩部分:伊甸區(qū)(Eden Space)和幸存者區(qū)(Survivor Space),所有的類(lèi)都是在伊甸區(qū)被new出來(lái)的铐然,幸存區(qū)有兩個(gè):0區(qū)和1區(qū)蔬崩,當(dāng)伊甸園的空間用完時(shí)恶座,程序又需要?jiǎng)?chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收(Minor GC)沥阳。將伊甸園中的剩余對(duì)象移動(dòng)到幸存0區(qū)跨琳,若幸存0區(qū)也滿(mǎn)了,再對(duì)該區(qū)進(jìn)行垃圾回收桐罕,然后移動(dòng)到1區(qū)脉让,那如果1區(qū)也滿(mǎn)了呢?(這里幸存0區(qū)和1區(qū)是一個(gè)互相交替的過(guò)程)再移動(dòng)到老年代,若老年代也滿(mǎn)了功炮,那么這個(gè)時(shí)候?qū)a(chǎn)生MajorGC(Full GC)溅潜,進(jìn)行老年區(qū)的內(nèi)存清理,若老年代執(zhí)行了Full GC后發(fā)現(xiàn)依然無(wú)法進(jìn)行對(duì)象的保存薪伏,就會(huì)產(chǎn)生OOM異常 “OutOfMemoryError ”伟恶。

如果出現(xiàn) java.lang.OutOfMemoryError:java heap space異常,說(shuō)明Java虛擬機(jī)的堆內(nèi)存不夠毅该,原因如下:

  1. Java虛擬機(jī)的堆內(nèi)存設(shè)置不夠博秫,可以通過(guò)參數(shù) -Xms(初始值大小),-Xmx(最大大小)來(lái)調(diào)整眶掌。
  2. 代碼中創(chuàng)建了大量大對(duì)象挡育,并且長(zhǎng)時(shí)間不能被垃圾收集器收集(存在被引用)或者死循環(huán)

永久代

永久存儲(chǔ)區(qū)是一個(gè)常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的Class朴爬,Interface的元數(shù)據(jù)即寒,也就是說(shuō)它存儲(chǔ)的是運(yùn)行環(huán)境必須的類(lèi)信息,被裝載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的召噩,關(guān)閉JVM才會(huì)釋 放此區(qū)域所占用的內(nèi)存母赵。

如果出現(xiàn)java.lang.OutOfMemoryError:PermGen space,說(shuō)明是Java虛擬機(jī)對(duì)永久代Perm內(nèi)存設(shè)置不夠具滴。一般出現(xiàn)這種情況凹嘲,都是程序啟動(dòng)需要加載大量的第三方j(luò)ar包,例如:在一個(gè)Tomcat下部署了太多的應(yīng)用构韵≈懿洌或者大量動(dòng)態(tài)反射生成的類(lèi)不斷被加載,最終導(dǎo)致Perm區(qū)被占滿(mǎn)疲恢。

注意:

Jdk1.6之前 : 有永久代凶朗,常量池1.6在方法區(qū)
Jdk1.7. : 有永久代,但是已經(jīng)逐步 “去永久代”显拳,常量池1.7在堆
Jdk1.8及之后: 無(wú)永久代棚愤,常量池1.8在元空間(1.8之前的永久代)

堆內(nèi)存調(diào)優(yōu)

-Xms :設(shè)置初始分配大小,默認(rèn)為物理內(nèi)存的“1/64”

-Xmx :最大分配內(nèi)存杂数,默認(rèn)為物理內(nèi)存的“1/4”

-XX:+PrintGCDetails :輸出詳細(xì)的GC處理日志

Dump內(nèi)存快照

在idea中也有這么一個(gè)插件宛畦, 就是JProfiler矛绘,一款性能瓶頸分析工具! 需要在idea里下載拆件,并在Settings–Tools–JProflier–JProflier executable中配置JProfile安裝可執(zhí)行文件

作用:

  1. 分析Dump文件刃永,快速定位內(nèi)存泄漏;
  2. 獲得堆中對(duì)象的統(tǒng)計(jì)數(shù)據(jù)
  3. 獲得對(duì)象相互引用的關(guān)系
  4. 采用樹(shù)形展現(xiàn)對(duì)象間相互引用的情況

代碼測(cè)試:

public class Demo03 {
    byte[] byteArray = new byte[1*1024*1024]; //1M = 1024K
    public static void main(String[] args) {
        ArrayList<Demo03> list = new ArrayList<>();
        int count = 0;
        try {
    while (true){
        list.add(new Demo03());
        count = count + 1;
}
}catch (Error e){
    System.out.println("count:"+count);
    e.printStackTrace();
}
}
}

vm參數(shù) :-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError

運(yùn)行結(jié)果

找到文件货矮,并用jprofiler打開(kāi),即可進(jìn)行分析

GC垃圾回收

GC作用域在于方法區(qū)和堆

JVM在進(jìn)行GC時(shí)斯够,并非每次都對(duì)上面三個(gè)內(nèi)存區(qū)域一起回收的囚玫,大部分時(shí)候回收的都是指新生代
因此GC按照回收的區(qū)域又分了兩種類(lèi)型,一種是普通的GC(minor GC)读规,一種是全局GC (major GC or Full GC)
普通GC:只針對(duì)新生代區(qū)域的GC
全局GC:針對(duì)老年代的GC抓督,偶爾伴隨對(duì)新生代的GC以及對(duì)永久代的GC

判斷垃圾可以回收的算法

引用計(jì)數(shù)法

基本概念:

引用計(jì)數(shù)是垃圾收集器中的早期策略。在這種方法中束亏,堆中每個(gè)對(duì)象實(shí)例都有一個(gè)引用計(jì)數(shù)铃在。當(dāng)一個(gè)對(duì)象被創(chuàng)建時(shí),就將該對(duì)象實(shí)例分配給一個(gè)變量碍遍,該變量計(jì)數(shù)設(shè)置為 1定铜。當(dāng)任何其它變量被賦值為這個(gè)對(duì)象的引用時(shí),計(jì)數(shù)加1(a = b怕敬,則 b 引用的對(duì)象實(shí)例的計(jì)數(shù)器加 1)揣炕,但當(dāng)一個(gè)對(duì)象實(shí)例的某個(gè)引用超過(guò)了生命周期或者被設(shè)置為一個(gè)新值時(shí),對(duì)象實(shí)例的引用計(jì)數(shù)器減 1东跪。任何引用計(jì)數(shù)器為 0 的對(duì)象實(shí)例可以被當(dāng)作垃圾收集畸陡。當(dāng)一個(gè)對(duì)象實(shí)例被垃圾收集時(shí),它引用的任何對(duì)象實(shí)例的引用計(jì)數(shù)器減 1虽填。

優(yōu)點(diǎn):

引用計(jì)數(shù)收集器可以很快的執(zhí)行丁恭,交織在程序運(yùn)行中。對(duì)程序需要不被長(zhǎng)時(shí)間打斷的實(shí)時(shí)環(huán)境比較有利斋日。

缺點(diǎn):

無(wú)法檢測(cè)出循環(huán)引用牲览。如父對(duì)象有一個(gè)對(duì)子對(duì)象的引用,子對(duì)象反過(guò)來(lái)引用父對(duì)象桑驱。這樣竭恬,他們的引用計(jì)數(shù)永遠(yuǎn)不可能為 0。

可達(dá)性分析算法

基本概念:

可達(dá)性分析算法是從離散數(shù)學(xué)中的圖論引入的熬的,程序把所有的引用關(guān)系看作一張圖,從一個(gè)節(jié)點(diǎn) GC ROOT 開(kāi)始赊级,尋找對(duì)應(yīng)的引用節(jié)點(diǎn)押框,找到這個(gè)節(jié)點(diǎn)以后,繼續(xù)尋找這個(gè)節(jié)點(diǎn)的引用節(jié)點(diǎn)理逊,當(dāng)所有的引用節(jié)點(diǎn)尋找完畢之后橡伞,剩余的節(jié)點(diǎn)則被認(rèn)為是沒(méi)有被引用到的節(jié)點(diǎn)盒揉,即無(wú)用的節(jié)點(diǎn),無(wú)用的節(jié)點(diǎn)將會(huì)被判定為是可回收的對(duì)象兑徘。

在Java語(yǔ)言中刚盈,可作為GC Roots的對(duì)象包括下面幾種:

  1. 虛擬機(jī)棧中引用的對(duì)象(棧幀中的本地變量表)
  2. 方法區(qū)中類(lèi)靜態(tài)屬性引用的對(duì)象
  3. 方法區(qū)中常量引用的對(duì)象
  4. 本地方法棧中 JNI(Native方法)引用的對(duì)象

常用的垃圾收集算法

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

標(biāo)記-清除算法采用從根集合(GC Roots)進(jìn)行掃描,對(duì)存活的對(duì)象進(jìn)行標(biāo)記挂脑,標(biāo)記完畢后藕漱,再掃描整個(gè)空間中未被標(biāo)記的對(duì)象,進(jìn)行回收崭闲。標(biāo)記-清除算法不需要進(jìn)行對(duì)象的移動(dòng)肋联,只需對(duì)不存活的對(duì)象進(jìn)行處理,在存活對(duì)象比較多的情況下極為高效刁俭,但由于標(biāo)記-清除算法直接回收不存活的對(duì)象橄仍,因此會(huì)造成內(nèi)存碎片。

缺點(diǎn):

這個(gè)算法需要暫停整個(gè)應(yīng)用牍戚,會(huì)產(chǎn)生內(nèi)存碎片侮繁。所謂的內(nèi)存碎片就是這塊哈希地址不可用了,出現(xiàn)零散的內(nèi)存碎片如孝。碎片最直接的問(wèn)題就是會(huì)導(dǎo)致無(wú)法分配大塊的內(nèi)存空間鼎天,以及程序運(yùn)行效率降低。
用通俗的話解釋一下 標(biāo)記/清除算法暑竟,就是當(dāng)程序運(yùn)行期間斋射,若可以使用的內(nèi)存被耗盡的時(shí)候,GC線程 就會(huì)被觸發(fā)并將程序暫停但荤,隨后將依舊存活的對(duì)象標(biāo)記一遍罗岖,最終再將堆中所有沒(méi)被標(biāo)記的對(duì)象全部清 除掉,接下來(lái)便讓程序恢復(fù)運(yùn)行腹躁。

復(fù)制算法(Copying)

當(dāng)對(duì)象在Eden(包括一個(gè)Survivor區(qū)域桑包,這里假設(shè)是From區(qū)域)出生后,在經(jīng)過(guò)一次Minor GC后纺非,如 果對(duì)象還存活哑了,并且能夠被另外一塊Survivor區(qū)域所容納 (上面已經(jīng)假設(shè)為from區(qū)域,這里應(yīng)為to區(qū)域烧颖,即to區(qū)域有足夠的內(nèi)存空間來(lái)存儲(chǔ)Eden 和 From 區(qū)域中存活的對(duì)象)弱左,則使用復(fù)制算法將這些仍然 還活著的對(duì)象復(fù)制到另外一塊Survivor區(qū)域(即 to 區(qū)域)中,然后清理所使用過(guò)的Eden 以及Survivor 區(qū)域(即form區(qū)域)炕淮,并且將這些對(duì)象的年齡設(shè)置為1拆火,以后對(duì)象在Survivor區(qū),每熬過(guò)一次Minor GC,就將這個(gè)對(duì)象的年齡 + 1们镜,當(dāng)這個(gè)對(duì)象的年齡達(dá)到某一個(gè)值的時(shí)候(默認(rèn)是15歲币叹,通過(guò)-XX:MaxTenuringThreshold 設(shè)定參數(shù))這些對(duì)象就會(huì)成為老年代。

如何判斷哪個(gè)是幸存區(qū)中的to區(qū)呢? 一句話:誰(shuí)空誰(shuí)是to

標(biāo)記壓縮(Mark-Compact)

在整理壓縮階段模狭,不再對(duì)標(biāo)記的對(duì)象作回收颈抚,而是通過(guò)所有存活對(duì)象都像一端移動(dòng),然后直接清除邊界 以外的內(nèi)存嚼鹉》泛海可以看到,標(biāo)記的存活對(duì)象將會(huì)被整理反砌,按照內(nèi)存地址依次排列雾鬼,而未被標(biāo)記的內(nèi)存會(huì)被 清理掉,如此一來(lái)宴树,當(dāng)我們需要給新對(duì)象分配內(nèi)存時(shí)策菜,JVM只需要持有一個(gè)內(nèi)存的起始地址即可,這比 維護(hù)一個(gè)空閑列表顯然少了許多開(kāi)銷(xiāo)酒贬。

分代收集算法

年輕代(Young Generation)的回收算法 (主要以 Copying 為主)

  1. 所有新生成的對(duì)象首先都是放在年輕代的又憨。年輕代的目標(biāo)就是盡可能快速的收集掉那些生命周期短的對(duì)象。
  2. 新生代發(fā)生的 GC 也叫做 Minor GC锭吨,MinorGC 發(fā)生頻率比較高(不一定等 Eden 區(qū)滿(mǎn)了才觸發(fā))

年老代(Old Generation)的回收算法(主要以 Mark-Compact 為主)

  1. 在年輕代中經(jīng)歷了 N 次垃圾回收后仍然存活的對(duì)象蠢莺,就會(huì)被放到年老代中。因此零如,可以認(rèn)為年老代中存放的都是一些生命周期較長(zhǎng)的對(duì)象
  2. 內(nèi)存比新生代也大很多(大概比例是1 : 2)躏将,當(dāng)老年代內(nèi)存滿(mǎn)時(shí)觸發(fā) Major GC 即 Full GC,F(xiàn)ull GC 發(fā)生頻率比較低考蕾,老年代對(duì)象存活時(shí)間比較長(zhǎng)祸憋,存活率標(biāo)記高
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肖卧,隨后出現(xiàn)的幾起案子蚯窥,更是在濱河造成了極大的恐慌,老刑警劉巖塞帐,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦赠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡葵姥,警方通過(guò)查閱死者的電腦和手機(jī)荷鼠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)牌里,“玉大人颊咬,你說(shuō)我怎么就攤上這事务甥∧盗桑” “怎么了喳篇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)态辛。 經(jīng)常有香客問(wèn)我麸澜,道長(zhǎng),這世上最難降的妖魔是什么奏黑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任炊邦,我火速辦了婚禮,結(jié)果婚禮上熟史,老公的妹妹穿的比我還像新娘馁害。我一直安慰自己,他們只是感情好蹂匹,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布碘菜。 她就那樣靜靜地躺著,像睡著了一般限寞。 火紅的嫁衣襯著肌膚如雪忍啸。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天履植,我揣著相機(jī)與錄音计雌,去河邊找鬼。 笑死玫霎,一個(gè)胖子當(dāng)著我的面吹牛凿滤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播庶近,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼翁脆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拦盹?” 一聲冷哼從身側(cè)響起鹃祖,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎普舆,沒(méi)想到半個(gè)月后恬口,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沼侣,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年祖能,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛾洛。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡养铸,死狀恐怖雁芙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞螟,我是刑警寧澤兔甘,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站鳞滨,受9級(jí)特大地震影響洞焙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拯啦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一澡匪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧褒链,春花似錦唁情、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至赛惩,卻和暖如春哀墓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喷兼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工篮绰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人季惯。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓吠各,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親勉抓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贾漏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容