欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java虛擬機(jī)學(xué)習(xí)筆記進(jìn)階篇

 更新時(shí)間:2019年06月07日 10:25:20   投稿:laozhang  
在本篇內(nèi)容里小編給大家分享了關(guān)于java虛擬機(jī)學(xué)習(xí)筆記的進(jìn)階內(nèi)容,需要的朋友們跟著學(xué)習(xí)下。

上一節(jié)是把大概的流程給過了一遍,但是還有很多地方?jīng)]有說到,后續(xù)的慢慢會涉及到,敬請期待!

這次我們說說垃圾收集器,又名gc,顧名思義,就是收集垃圾的容器,那什么是垃圾呢?在我們這里指的就是堆中那些沒人要的對象。

1.垃圾收集器的由來

為什么要有垃圾收集器???不知道有沒有想過這個(gè)問題,你說我運(yùn)行一個(gè)程序要什么垃圾收集器???

隨意看一下下面兩行代碼:

User user = new User("root","123456")
user = new User("lisi","123123")

簡單畫一下內(nèi)存圖,可以看到user這個(gè)局部變量本來是指向root這個(gè)對象,現(xiàn)在改為指向lisi這個(gè)對象,那么此時(shí)這個(gè)root對象沒有人用,假如類似root這樣的對象非常多的話,那么jvm性能就會越來越低,直至最后創(chuàng)建個(gè)對象可能都要十幾秒,而且堆內(nèi)存總有一天會裝滿就會報(bào)內(nèi)存溢出異常;

所以我們就要想辦法把類似root這種對象給清理掉,這樣才能保證jvm高效的運(yùn)行;

假如虛擬機(jī)沒有提供gc你覺得會怎么樣?其實(shí)也行,只不過你每次需要你用代碼手動釋放不需要的對象,關(guān)于這點(diǎn)有好處有壞處,好處就是有利于我們對堆內(nèi)存的控制,壞處就是我們在一些比較復(fù)雜的程序之中由于手動釋放內(nèi)存難免會出錯,但是這中錯誤還不怎么明顯,可能要你去慢慢調(diào)試好久才能看到!

所以java就把這種工作自己處理了,讓一個(gè)gc線程一直在后臺運(yùn)行,隨時(shí)準(zhǔn)備清理不需要用的對象,雖然相當(dāng)程度上會對jvm性能造成一些影響,但是由于gc太好用了,我們不用再人為的去關(guān)心垃圾對象的釋放,簡化了我們編寫程序的難度,所以這種影響程度完全可以接受!

這里順便一提兩個(gè)基本概念,內(nèi)存泄漏和內(nèi)存溢出:

內(nèi)存溢出(Memory Overflow)比較好理解,就是我們保存對象需要的空間太大了,但是申請內(nèi)存比較小,于是裝不下,于是就會報(bào)內(nèi)存溢出異常,比如說你申請了一個(gè)integer,但給它存了long才能存下的數(shù),那就是內(nèi)存溢出;專業(yè)點(diǎn)的說法就是:你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿足需求,于是產(chǎn)生溢出。

內(nèi)存泄漏(Memory Leak)指的就是我們new出來的對象保存在堆中但是沒有釋放,于是堆中內(nèi)存會越來越少,會導(dǎo)致系統(tǒng)運(yùn)行速度減慢,嚴(yán)重情況會使程序卡死;專業(yè)點(diǎn)的說法就是:你用malloc或new申請了一塊內(nèi)存,但是沒有通過free或delete將內(nèi)存釋放,導(dǎo)致這塊內(nèi)存一直處于占用狀態(tài)。

對于我們jvm來說,通常情況下我們不用擔(dān)心內(nèi)存泄漏,因?yàn)橛幸粋€(gè)強(qiáng)大的gc在我們程序的背后默默地為我們清理,但是也會有特殊情況,比如當(dāng)被分配的對象可達(dá)但已無用(未對作廢數(shù)據(jù)內(nèi)存單元的賦值null)即會引起,至于這個(gè)可達(dá)是什么意思,后面會慢慢說到;

相對而言內(nèi)存溢出我們比較常見,還有g(shù)c只會對堆內(nèi)存進(jìn)行回收,所以靜態(tài)變量是不會回收的;

再順便提一下另外兩個(gè)小概念,非守護(hù)線程(也叫用戶線程)和守護(hù)線程,看下面這個(gè)丑陋的程序運(yùn)行會有幾個(gè)線程???

public class User{
 public static void main(String[] args){
  System.out.println("我是java小新人");
 }
}

兩個(gè)線程,一個(gè)是執(zhí)行main方法的線程,后臺還有g(shù)c執(zhí)行g(shù)c的線程,在這里,用戶線程就是執(zhí)行main方法的那個(gè)線程,執(zhí)行g(shù)c的線程就是守護(hù)線程,默默地守護(hù)者jvm,假如jvm是雅典娜,那么守護(hù)線程就是黃金圣斗士;

當(dāng)用戶線程停止之后整個(gè)程序直接停止,守護(hù)線程也會終止;但是黃金圣斗士掛了雅典娜還是可以好好活著的繼續(xù)愉快的玩耍的;

2.堆內(nèi)存結(jié)構(gòu)

哎,內(nèi)存中的結(jié)構(gòu)如果真的要通過源代碼去看,簡直讓人崩潰,除了專業(yè)搞這方面的不然真的很難懂,本來我想自己畫一下草圖了,發(fā)現(xiàn)太丑陋了,于是去順手借了一張圖:

途中可以很清楚的看到,整塊堆內(nèi)存分為年輕人聚集的地方和老年人聚集的地方,年輕人比較少趨勢占用1/3空間(新生代),老年人比較多就占用2/3的空間(老年代),然而啊,年輕人又要分分類,分別是Eden區(qū)占新生代8/10,F(xiàn)rom Survivor區(qū)占新生代1/10,To Survivor區(qū)占新生代1/10,emmm。。。我特意查了一下百度翻譯,Eden---->樂園,Survivor----->幸存者;哦~~~我感覺我仿佛明白了命名人的意圖!

那么新生代和老年代到底是干什么的呢?我們創(chuàng)建的對象是放在哪里?。?/p>

新生代:java對象申請內(nèi)存和存放對象的地方,而且存放的對象都是那種死的比較快的對象,很多時(shí)候創(chuàng)建沒多久就清理掉了,那些活的時(shí)間比較長的對象都被移動到了老年代。

老年代:存大對象比如長字符串、數(shù)組由于需要大量連續(xù)的內(nèi)存空間,可以直接進(jìn)入老年代;還有長期存活的對象也會進(jìn)入老年代,具體是多長時(shí)間呢,其實(shí)默認(rèn)就是經(jīng)過15 對新生代的清理(Minor Gc)還能活著的對象。

而垃圾收集器對這兩塊內(nèi)存有兩種行為,一種是對新生代的清理,叫做Minor Gc,另外一種是對老年代的清理被叫做Major Gc。

順便提一點(diǎn):很多博客中都把Major GC和Full GC說成是一種,其實(shí)還是有區(qū)別的,因?yàn)楹芏鄇ava虛擬機(jī)的實(shí)現(xiàn)不一樣,所以就有各種各樣的名稱,比如Minor Gc又叫做Young GC,Major GC也可以叫做Old GC,但是Full GC卻有點(diǎn)不同,F(xiàn)ull GC 是清理整個(gè)堆空間 —— 包括年輕代、老年代和永久代(也叫做方法區(qū))。因此 Full GC 可以說是 Minor GC 和 Major GC 的結(jié)合。當(dāng)然在我們這里,為了好理解我們也就把Full GC當(dāng)作Major GC就可以了?!?/p>

3.篩選清理對象

GC要工作的話,必須首先知道哪些對象要被清理,你想一下,在新生代和老年代有這么多對象,怎么篩選會又快又省事呢?可以有以下兩種方法

1.引用計(jì)數(shù)算法,相當(dāng)于給你創(chuàng)建的對象偷偷的添加一個(gè)計(jì)數(shù)器,每引用一次這個(gè)對象,計(jì)數(shù)器就加一,引用失效就減一,當(dāng)這個(gè)計(jì)數(shù)器為0的時(shí)候,說明這個(gè)對象沒有變量引用了,于是我們就可以說這個(gè)對象可以被清理了

2.根搜索算法(jvm用的就是這個(gè)),這個(gè)怎么理解呢?你可以想象現(xiàn)在有一個(gè)數(shù)組,這個(gè)數(shù)組里面包含了一些東西的引用,我們將這個(gè)數(shù)組叫做”GC Root“,然后我們根據(jù)這個(gè)數(shù)組中的引用去找到對應(yīng)的對象,看看這個(gè)對象中又引用了哪些對象,一直往下找,這樣就形成了很多線路,在這個(gè)線路上的對象就叫做”可達(dá)對象“,不在這個(gè)線路上的對象就是不可達(dá)對象,而不可達(dá)對象也就是我們要清理的對象;

其中可以作為GC Root的對象:

(1).類中的靜態(tài)變量,當(dāng)它持有一個(gè)指向一個(gè)對象的引用時(shí),它就作為root

(2).活動著的線程,可以作為root

(3).一個(gè)Java方法的參數(shù)或者該方法中的局部變量,這兩種對象可以作為root

(4).JNI方法中的局部變量或者參數(shù),這兩種對象可以作為root

(5).其它。

關(guān)于這個(gè)根搜索算法專業(yè)一點(diǎn)的說法就是:通過一系列的名為“GC Root”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所有走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對象到GC Root沒有任何引用鏈相連時(shí)(用圖論來說就是GC Root到這個(gè)對象不可達(dá)時(shí)),證明該對象是可以被回收的。

4.進(jìn)行垃圾回收

前面已經(jīng)篩選出了我們要清理的對象,但是怎么清理比較快呢?難道要一個(gè)一個(gè)對象慢慢刪除嘛?就好像你要清理手機(jī)中的垃圾,你會一個(gè)應(yīng)用一個(gè)應(yīng)用去慢慢清理數(shù)據(jù)嗎?當(dāng)然不可能,這也太浪費(fèi)時(shí)間了!我們當(dāng)然是用手機(jī)管家或者360管家先把要清理的東西給收集起來放在一起,然后我們一清理就是全部,一個(gè)字,爽!

ok,在這里也一樣,我們要想辦法把所有的要清理的對象給放在一起清理,有什么辦法呢?

1.標(biāo)記-----清除算法:這種方法分為兩步,先標(biāo)記然后清除,其實(shí)就是需要回收的對象標(biāo)記一下,然后就是把有標(biāo)記的對象全部清理即可;這種方式比較適合對象比較少的內(nèi)存,假如對象太多標(biāo)記都要好半天,更別說清除了,而且用這種方法清除的內(nèi)存空間會東一塊西一塊,下次再創(chuàng)建一個(gè)大的對象可能會出問題1

2.復(fù)制算法:按內(nèi)存容量將內(nèi)存劃分為等大小的兩塊。每次只使用其中一塊,當(dāng)這一塊內(nèi)存滿后將尚存活的對象復(fù)制到另一塊上去,把已經(jīng)使用的那塊內(nèi)存直接全部清理掉;這種方法最大的缺陷就是耗內(nèi)存啊,只能用總內(nèi)存的一半,而且如果對象很多復(fù)制都要花很多時(shí)間。

3.標(biāo)記----整理算法:結(jié)合以上兩種方法優(yōu)缺點(diǎn)進(jìn)行改良的一種方法,標(biāo)記和第一種方法一樣把要清理的對象做好標(biāo)記,然后把所有標(biāo)記的對象移動到本內(nèi)存的一個(gè)小角落,最后集中力量對那個(gè)小角落進(jìn)行消滅

4.分代收集算法:這是集中了上面三種方法的優(yōu)點(diǎn)所實(shí)現(xiàn)的一種最好的方法,是目前大部分JVM所采用的方法,這種算法的核心思想是根據(jù)對象存活的時(shí)間不同將內(nèi)存劃分為不同的域,一般情況下將GC堆劃分為新生代和老年代;新生代的特點(diǎn)是每次垃圾回收時(shí)都有大量垃圾需要被回收,少數(shù)對象存活,因此可以使用復(fù)制算法;老年代的特點(diǎn)是每次垃圾回收時(shí)只有少量對象需要被回收,可以選用”標(biāo)記--清除方法“”或者標(biāo)記--整理算法“

所以目前大部分JVM的GC都是使用分代收集算法。

5.執(zhí)行GC的步驟

前面說了這么多無非是介紹堆的內(nèi)部結(jié)構(gòu),然后怎么找到要被清理的對象,然后為了提高效率怎么清理最快!

現(xiàn)在我們就大概說說GC的清理步驟(詳細(xì)版):

1.我們創(chuàng)建對象的時(shí)候會進(jìn)行一個(gè)判斷,極少數(shù)很大的對象直接放進(jìn)老年代中,除此之外所有新創(chuàng)建的對象都放進(jìn)新生代的Eden區(qū)中;

2.此時(shí)新生代中只有Eden區(qū)中有對象,兩個(gè)Survivor區(qū)中是空的;當(dāng)我們創(chuàng)建了很多對象,使得Eden區(qū)快滿的時(shí)候第一次GC發(fā)生(就是執(zhí)行了一次Minior GC),Eden區(qū)和”From“區(qū)(此時(shí)“From”區(qū)是空的)存活的對象將會被移動到Surviver區(qū)的“To”區(qū),并且為每個(gè)對象設(shè)置一個(gè)計(jì)數(shù)器記錄年齡,初始值為1;每進(jìn)行一次GC,會給那些存活的對象設(shè)置一個(gè)年齡+1 的操作,默認(rèn)是當(dāng)年齡達(dá)到15歲,下次GC就會直接把這種”老油條“丟到老年代中。

3.Minior GC之后,會進(jìn)行一個(gè)比較厲害的操作,就是將”To“區(qū)和”From“換個(gè)名字,沒錯,就是換個(gè)名字,然后進(jìn)行下一次Minior GC。

4.由于又創(chuàng)建了很多對象使得Eden區(qū)要滿了,于是又一次Minior GC,Eden區(qū)還存活的對象會直接移動到Surviver區(qū)的“To”區(qū),此時(shí)”From“區(qū)(這里就是交換名字之前的”To“區(qū))中的對象有兩個(gè)地方可以去,要么年齡滿15歲了去老年代,要么就移動到”To“區(qū)

5.此時(shí)我們看一下,只有”To“區(qū)的對象是活著的,Eden區(qū)都是垃圾對象可以直接全部清理,”From“區(qū)是空的;不管怎樣,在進(jìn)行下一次Minior GC之前保證名為”To“的Survivor區(qū)域是空的就ok了

6.當(dāng)老年代中快要裝滿之后,就會進(jìn)行一次Major GC,這個(gè)清理事件很慢,至少比Minior GC慢十幾倍,甚至更多,所以我們盡量要少執(zhí)行Major GC

注意:如果在移動過程中”To“ 區(qū)被填滿了,剩余的對象會被直接移動到老年代中。還有在每次Minior GC之前會先進(jìn)性判斷,只要老年代里面的連續(xù)空間大于新生代對象總大小或者歷次晉升的平均大小進(jìn)行Minor GC,否則進(jìn)行Major GC。

簡化版:

(1)Eden 區(qū)活著的對象 + From Survivor 存儲的對象被復(fù)制到 To Survivor ;

(2)清空 Eden 和 From Survivor ;

(3)顛倒 From Survivor 和 To Survivor 的邏輯關(guān)系: From 變 To , To 變 From 。

(4)老年代的Major GC執(zhí)行時(shí)間很長,盡量少執(zhí)行

只有在Eden空間快滿的時(shí)候才會觸發(fā) Minor GC 。而 Eden 空間占新生代的絕大部分,所以 Minor GC 的頻率得以降低。當(dāng)然,使用兩個(gè) Survivor 這種方式我們也付出了一定的代價(jià),如 10% 的空間浪費(fèi)、復(fù)制對象的開銷等。

6.知識點(diǎn)補(bǔ)充

通過查看了很多大佬的博客看到的很多有關(guān)的東西還是挺有趣的,于是簡單做個(gè)小筆記:

6.1.新創(chuàng)建的對象是在堆中的新生代的Eden區(qū),由于堆中內(nèi)存是所有線程共享,所以在堆中分配內(nèi)存需要加鎖。而Sun JDK為提升效率,會為每個(gè)新建的線程在Eden上分配一塊獨(dú)立的空間由該線程獨(dú)享,這塊空間稱為TLAB(Thread Local Allocation Buffer)。在TLAB上分配內(nèi)存不需要加鎖,因此JVM在給線程中的對象分配內(nèi)存時(shí)會盡量在TLAB上分配。如果對象過大或TLAB用完,則仍然在堆上Eden區(qū)或者老年代進(jìn)行分配。如果Eden區(qū)內(nèi)存也用完了,則會進(jìn)行一次Minor GC(young GC)。

6.2.很多人認(rèn)為方法區(qū)(或者HotSpot虛擬機(jī)中的永久代)是沒有垃圾收集的,Java虛擬機(jī)規(guī)范中確實(shí)說過可以不要求虛擬機(jī)在方法區(qū)實(shí)現(xiàn)垃圾收集,而且在方法區(qū)進(jìn)行垃圾收集的“性價(jià)比”一般比較低:在堆中,尤其是在新生代中,常規(guī)應(yīng)用進(jìn)行一次垃圾收集一般可以回收70%~95%的空間,而永久代的垃圾收集效率遠(yuǎn)低于此。

6.3對象調(diào)用.finalize方法被調(diào)用后,對象一定會被回收嗎?

在經(jīng)過可達(dá)性分析后,到GC Roots不可達(dá)的對象可以被回收(但并不是一定會被回收,至少要經(jīng)過兩次標(biāo)記),此時(shí)對象被第一次標(biāo)記,并進(jìn)行一次判斷,如果該對象沒有調(diào)用過或者沒有重寫finalize()方法,那么在第二次標(biāo)記后可以被回收了;否則,該對象會進(jìn)入一個(gè)FQueue中,稍后由JVM建立的一個(gè)Finalizer線程中去執(zhí)行回收,此時(shí)若對象中finalize中“自救”,即和引用鏈上的任意一個(gè)對象建立引用關(guān)系,到GC Roots又可達(dá)了,在第二次標(biāo)記時(shí)它會被移除“即將回收”的集合;如果finalize中沒有逃脫,那就面臨被回收。因此finalize方法被調(diào)用后,對象不一定會被回收。

6.4.如果在Survivor空間中相同年齡所有對象大小總和大于Survivor空間的一半,年齡大于或者等于該年齡的對象直接進(jìn)入老年代。不需要等到15歲。

總結(jié)

這篇說的就是java虛擬機(jī)怎么去收集對內(nèi)存的垃圾,首先是要通過可達(dá)性分析判斷哪些對象是可達(dá)的,哪些是不可達(dá)的,那些不可達(dá)的對象就是我們要處理的對象!這些不可達(dá)對象可能在新生代和老年代都有,在新生代用復(fù)制算法去處理垃圾,老年代用標(biāo)記整理算法處理垃圾,這種處理方式也可以叫做分代收集算法!而且還簡單說了一下Minor GC和Major GC的觸發(fā)方式!

基本的東西就這么多,假如要深入的話可以深入很多,比如我們可以控制新生代的大小,還有很多種垃圾處理器的實(shí)現(xiàn)產(chǎn)品等等,都是可以去慢慢了解的。

相關(guān)文章

  • 詳解IDEA中SpringBoot整合Servlet三大組件的過程

    詳解IDEA中SpringBoot整合Servlet三大組件的過程

    這篇文章主要介紹了詳解IDEA中SpringBoot整合Servlet三大組件的過程,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 查看Java所支持的語言及相應(yīng)的版本信息

    查看Java所支持的語言及相應(yīng)的版本信息

    Java語言作為第一種支持國際化的語言,在Internet從一開始就具有其他語言無與倫比的國際化的本質(zhì)特性,查看Java所支持的語言及相應(yīng)的版本信息可以采用以下代碼進(jìn)行查詢
    2014-01-01
  • Struts2+uploadify多文件上傳實(shí)例

    Struts2+uploadify多文件上傳實(shí)例

    這篇文章主要為大家詳細(xì)介紹了Struts2+uploadify多文件上傳實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • maven如何在tomcat8中實(shí)現(xiàn)自動部署

    maven如何在tomcat8中實(shí)現(xiàn)自動部署

    本篇文章主要介紹了maven如何在tomcat8中實(shí)現(xiàn)自動部署,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • 詳解使用Spring Boot開發(fā)Restful程序

    詳解使用Spring Boot開發(fā)Restful程序

    本篇文章主要介紹了詳解使用Spring Boot開發(fā)Restful程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-05-05
  • Java 和 Kotlin Lambda 表達(dá)式示例詳解

    Java 和 Kotlin Lambda 表達(dá)式示例詳解

    Lambda 表達(dá)式是一種簡潔的函數(shù)表達(dá)方式,可以把函數(shù)作為一個(gè)方法的參數(shù),或者將代碼塊轉(zhuǎn)換為數(shù)據(jù)傳遞,這篇文章主要介紹了Java 和 Kotlin Lambda 表達(dá)式示例詳解,需要的朋友可以參考下
    2024-06-06
  • 基于接口實(shí)現(xiàn)java動態(tài)代理示例

    基于接口實(shí)現(xiàn)java動態(tài)代理示例

    這篇文章主要介紹了基于接口實(shí)現(xiàn)java動態(tài)代理示例,需要的朋友可以參考下
    2014-04-04
  • Java的Servlet及其生命周期詳解

    Java的Servlet及其生命周期詳解

    這篇文章主要介紹了Java的Servlet及其生命周期詳解,Servlet是用Java編寫的服務(wù)器端程序,一門用于開發(fā)動態(tài)web資源的技術(shù),其主要功能在與交互式的瀏覽和修改數(shù)據(jù),生成動態(tài)web內(nèi)容,需要的朋友可以參考下
    2023-11-11
  • 手動添加jar包進(jìn)Maven本地庫內(nèi)的方法

    手動添加jar包進(jìn)Maven本地庫內(nèi)的方法

    這篇文章主要介紹了手動添加jar包進(jìn)Maven本地庫內(nèi)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java異常處理詳細(xì)介紹及實(shí)例

    java異常處理詳細(xì)介紹及實(shí)例

    這篇文章主要介紹了java異常處理詳細(xì)介紹及實(shí)例的相關(guān)資料,本文對java異常進(jìn)行了知識層次的總結(jié),需要的朋友可以參考下
    2017-04-04

最新評論