目錄
一.JVM的概念
什么是JVM?
二.JVM的運行流程
1.class文件如何被JVM加載并運行
2.JVM運行時數(shù)據(jù)包括哪些區(qū)域(M)
三.類加載的過程(M)
四.雙親委派模型
1.雙親委派模型分析
2.JAVA中有哪些類加載器(M)
五.垃圾回收機(jī)制
1.死亡對象的標(biāo)識
①引用計數(shù)算法
②可達(dá)性分析算法
?什么是GC Roots 呢?
GC Roots一般有以下幾種類型:
2.垃圾回收的算法
①標(biāo)記清除算法
②標(biāo)記復(fù)制算法
③標(biāo)記整理算法
?垃圾回收的過程?(M)
1.Minor GC(新生代的垃圾回收)
?2.Full GC(老年代的垃圾回收)
3.垃圾回收器
介紹一下垃圾回收器(M)
?主要的垃圾回收器有以下幾種:
一.JVM的概念
什么是JVM?
JVM是JavaVirtualMachine(Java虛擬機(jī))的縮寫,JVM是一種用于計算設(shè)備的規(guī)范,它是一個虛構(gòu)出來的計算機(jī),是通過在實際的計算機(jī)上仿真模擬各種計算機(jī)功能來實現(xiàn)的。?
二.JVM的運行流程
1.class文件如何被JVM加載并運行
①當(dāng).java文件被編譯為.class文件時,.class文件會被加載到類加載子系統(tǒng),然后由類加載子系統(tǒng)將文件加載到運行時數(shù)據(jù)區(qū)
②在運行時數(shù)據(jù)區(qū)中,類對象被加載到方法區(qū)中,便于后面new出來的實例對象可以通過這個類對象模板中創(chuàng)建新的對象。
③創(chuàng)建出來實例對象會被加載到堆中
④虛擬機(jī)棧:每個線程都會在虛擬機(jī)棧中開辟一個空間,每調(diào)用一個方法時,這個方法就會被壓入棧中,也就是說每個棧中存放的是方法調(diào)用的層級
⑤本地方法棧:同樣是每一個線程都會在本地方法棧中開辟一塊內(nèi)存,每次調(diào)用一個本地方法,這個本地方法就會被加載到本地方法棧中。
⑥程序計數(shù)器:記錄當(dāng)前線程所運行到的指令地址:由于考慮到j(luò)ava虛擬機(jī)在多線程模式下是通過線程輪流切換并分配時間片的方式進(jìn)行的,因此當(dāng)某個線程分配的時間片使用完但是當(dāng)前線程并沒有執(zhí)行結(jié)束時,這時就需要使用程序計數(shù)器記錄下當(dāng)前線程所運行到的指令地址,當(dāng)當(dāng)前線程再度被分配到時間片時,從當(dāng)前指令下繼續(xù)執(zhí)行。
2.JVM運行時數(shù)據(jù)包括哪些區(qū)域(M)
①方法區(qū):在JDK1.7之前, 方法區(qū)又被稱作永久代,在1.8及之后被稱為元空間,區(qū)別在于實現(xiàn)方式的不同,方法區(qū)是當(dāng)類對象被加載到JVM時存儲的地方,類對象被存儲到方法區(qū)中以便后續(xù)需要創(chuàng)建實例對象時直接從方法區(qū)中的類對象獲取并創(chuàng)建實例對象。需要說明的是,方法區(qū)是所有線程所共享的
②堆:創(chuàng)建的實例對象都會被加載到堆空間中,堆空間的大小可以通過JVM中的參數(shù)進(jìn)行設(shè)置:Xms10(最小堆內(nèi)存空間) 是設(shè)置堆空間大小,Xmx10(最大堆內(nèi)存空間)也是設(shè)置堆空間大小的:通常我們將這兩個內(nèi)存參數(shù)設(shè)置為同一個大小,我們一般將兩個參數(shù)均設(shè)置為線程運行可能消耗的最大堆空間,如果內(nèi)存空間比較小時,可能會出現(xiàn)OOM錯誤(OUT OF MEMORY ERROR),一旦出現(xiàn)這個錯誤,我們可以將堆空間設(shè)置的大一點。
③java虛擬機(jī)棧:每個線程都會在虛擬機(jī)棧中開辟一個空間,每調(diào)用一個方法時,這個方法就會被壓入棧中,也就是說每個棧中存放的是方法調(diào)用的層級,虛擬機(jī)棧容量的大小一般由Xss這個參數(shù)確定,如果棧溢出,會報出StackOverFlow錯誤
④本地方法棧:同樣是每一個線程都會在本地方法棧中開辟一塊內(nèi)存,每次調(diào)用一個本地方法,這個本地方法就會被加載到本地方法棧中。
⑤程序計數(shù)器:記錄當(dāng)前線程所運行到的指令地址:由于考慮到j(luò)ava虛擬機(jī)在多線程模式下是通過線程輪流切換并分配時間片的方式進(jìn)行的,因此當(dāng)某個線程分配的時間片使用完但是當(dāng)前線程并沒有執(zhí)行結(jié)束時,這時就需要使用程序計數(shù)器記錄下當(dāng)前線程所運行到的指令地址,當(dāng)當(dāng)前線程再度被分配到時間片時,從當(dāng)前指令下繼續(xù)執(zhí)行。
三.類加載的過程(M)
1.加載
將所有的.class文件全部加載到虛擬機(jī)中
2.驗證
根據(jù).class文件的規(guī)范對當(dāng)前的文件進(jìn)行驗證
3.準(zhǔn)備
將各種類型的值初始化為默認(rèn)的值,int 初始化為0,float初始化為0.0f.......
4.解析
將java虛擬機(jī)中常量池的符號引用替換為直接引用,也就是初始化常量池的過程。(例如:原本常量池中的字符串并沒有直接引用,(還是占位符),在這個過程中將創(chuàng)建一個新的字符串并將常量池中的占位符進(jìn)行替換)
?
5. 初始化
之前的工作都是準(zhǔn)備工作,這一步是真正執(zhí)行類中的java代碼,經(jīng)過這一步,真正的java對象才會被創(chuàng)建出來
四.雙親委派模型
1.雙親委派模型分析
不同的類在加載時會使用不同的類加載器:
?其類加載器的策略如下:當(dāng)創(chuàng)建一個類時,先從applicationClassLoader開始向上轉(zhuǎn)發(fā),一直到BootStrapClassLoader,BootStrapClassLoader在自己的加載路徑中查找是否存在這個類,有則加載,沒有則向下轉(zhuǎn)發(fā)到ExtClassLoader,ExtClassLoader在自己的加載路徑中查找有沒有這個類,有則加載,沒有則向下轉(zhuǎn)發(fā)到ApplicationClassLoader,在自己的路徑中查找并加載這個類
2.JAVA中有哪些類加載器(M)
五.垃圾回收機(jī)制
1.死亡對象的標(biāo)識
①引用計數(shù)算法
概念:當(dāng)對象被引用,那么其引用的次數(shù)就加1,當(dāng)引用的對象為0的時候,就將這個對象標(biāo)識為死亡對象,這個對象就該被回收了。
圖示如下:
?過程分析:當(dāng)創(chuàng)建兩個對象,定義兩個變量指向創(chuàng)建的兩個對象,此時這兩個對象的引用都+1,同時使用對象的instance屬性也指向這兩個創(chuàng)建的對象,這時這兩個新創(chuàng)建的對象的引用次數(shù)都是2了,但是如果對這兩個變量置為空,則兩個新創(chuàng)建的對象的引用次數(shù)-1,但是由于兩個變量都為空了,兩個變量的instance屬性也無法訪問到這兩個對象了,但是由于這兩個對象的引用次數(shù)并沒有變?yōu)?(instance屬性仍然指向兩個對象),因此這兩個對象無法進(jìn)行回收,這也就造成了內(nèi)存泄漏的問題
notes:內(nèi)存泄漏:內(nèi)存泄漏(Memory Leak)是指程序中已動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放,造成系統(tǒng)內(nèi)存的浪費,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果。
內(nèi)存泄漏問題在程序剛開始運行時并不容易發(fā)現(xiàn),當(dāng)程序運行了一段時間,堆空間不斷放入新的對象而無法及時清除,最終會導(dǎo)致堆空間被占滿,導(dǎo)致程序崩潰。
②可達(dá)性分析算法
可達(dá)性分析算法的實現(xiàn)原理如下:從GC Roots(起始結(jié)點集)根據(jù)引用關(guān)系從上往下搜尋,搜尋的路徑稱為【引用鏈】,如果某個結(jié)果到任何一個根對象都沒有引用鏈,則稱這個對象不可達(dá),標(biāo)識這個對象不可達(dá)。
在垃圾回收時首先會搜尋到所有的根結(jié)點【枚舉根結(jié)點】,然后從這些根結(jié)點從上向下搜尋,這個過程需要暫停用戶線程,即觸發(fā)STW,能搜尋到說明這個對象保留,如果不能搜尋這個對象則使用垃圾回收的算法對其進(jìn)行回收。
?什么是GC Roots 呢?
GC Roots也是對象,而且是JVM一定不能回收的對象,在JVM進(jìn)行GC的時候,在所有用戶到達(dá)safepoint之后首先會進(jìn)行STW,暫停所有用戶的線程,之后進(jìn)行對根結(jié)點的枚舉,之后再從根結(jié)點從上往下搜尋。
GC Roots一般有以下幾種類型:
1、方法區(qū)靜態(tài)屬性引用的對象
全局對象的一種,Class對象本身很難被回收,回收的條件非??量?,只要Class對象不被回收,靜態(tài)成員就不能被回收。
2、方法區(qū)常量池引用的對象
也屬于全局對象,例如字符串常量池,常量本身初始化后不會再改變,因此作為GC Roots也是合理的。
3、方法棧中棧幀本地變量表引用的對象
屬于執(zhí)行上下文中的對象,線程在執(zhí)行方法時,會將方法打包成一個棧幀入棧執(zhí)行,方法里用到的局部變量會存放到棧幀的本地變量表中。只要方法還在運行,還沒出棧,就意味這本地變量表的對象還會被訪問,GC就不應(yīng)該回收,所以這一類對象也可作為GC Roots。
4、JNI本地方法棧中引用的對象
和上一條本質(zhì)相同,無非是一個是Java方法棧中的變量引用,一個是native方法(C、C++)方法棧中的變量引用。
5、被同步鎖持有的對象
被synchronized鎖住的對象也是絕對不能回收的,當(dāng)前有線程持有對象鎖呢,GC如果回收了對象,鎖就失效了。
2.垃圾回收的算法
①標(biāo)記清除算法
算法思路:標(biāo)記當(dāng)前所有對象中的不可用對象,在原位將其進(jìn)行標(biāo)注,之后進(jìn)行清除
缺陷:但是這個算法的缺陷也比較明顯:在原位將不可用對象進(jìn)行了清除,會產(chǎn)生大量不連續(xù)的可用空間碎片,如果在這時需要創(chuàng)建較大的對象但是當(dāng)前連續(xù)的可用空間不滿足時,則需要觸發(fā)下一次垃圾回收。
?
②標(biāo)記復(fù)制算法
算法思路:將全部的可用空間劃分為兩部分,比如劃分為空間1和空間2,空間1在清除完不可用對象后,存在大量的不連續(xù)的可用空間的碎片,空間2對空間1中的可用和不可用對象進(jìn)行分類整理排列,在排序完成后將空間1進(jìn)行整體清除,后面再次清除時重復(fù)這種操作
缺陷:這種算法的缺陷也相對明顯,再每一次垃圾回收的過程中,只有一半的空間能真正發(fā)揮作用
③標(biāo)記整理算法
算法思路如下:在每次清除完部分的不可用對象之后就對全部的對象進(jìn)行排序整理
缺陷:每次清除都要進(jìn)行排序整理,算法效率相對較低
?垃圾回收的過程?(M)
?不同的類對象進(jìn)行了分代處理,將所有的類對象分為了新生代和老年代,其中新生代占堆空間的1/3,老年代占堆空間的2/3,新生代的空間中還有1/5的survivor(幸存者分為from和to兩個區(qū)域)區(qū)域。對象進(jìn)行了分代處理,垃圾回收也被劃分為兩種類型:①Minor GC(新生代的垃圾回收)②Full GC(Major GC/Full GC 發(fā)生在老年代的垃圾回收)。
1.Minor GC(新生代的垃圾回收)
新生代的垃圾回收采用的是復(fù)制算法(因為新生代中的對象迭代比較頻繁,所以需要效率相對較高的算法,雖然復(fù)制算法所消耗的空間較大,但是其效率相對較高),其過程如下:
①創(chuàng)建對象時在Eden區(qū)直接創(chuàng)建,此時survivor兩塊區(qū)域都為空(空間大小之比:Eden:from:to=8:1:1)
②當(dāng)我們再次創(chuàng)建對象,發(fā)現(xiàn)Eden區(qū)當(dāng)前的容量不足以存放當(dāng)前對象,則將Eden區(qū)的對象進(jìn)行判斷處理,對判定死亡的對象進(jìn)行清除,存活的對象將其移至survivor區(qū)中的from區(qū),移動到from區(qū)的對象的“年齡”+1;
③Eden區(qū)持續(xù)創(chuàng)建新的對象,并對沒有引用的對象進(jìn)行清除之后,Eden區(qū)存活的對象都被移動到了from區(qū),Eden區(qū)和from區(qū)都被占用滿了,這時要把Eden區(qū)和from區(qū)存活的對象移動到to區(qū),然后將Eden區(qū)和from區(qū)都進(jìn)行清空,在上一輪中被放置在from區(qū)的對象的年齡再加1
⑤在下一輪GC中會將from區(qū)和to區(qū)的位置互換,此時to區(qū)為空,from區(qū)和Eden區(qū)繼續(xù)參與下一輪的GC ,重復(fù)上面的步驟
⑥經(jīng)過不斷的GC,在新生代中的對象的年齡達(dá)到了一定的閾值(默認(rèn)閾值是15),這時候就要將這些對象移動到老年代
?總結(jié):我們縱觀整個新生代垃圾處理的過程來看:在每一輪的GC中,新的對象會在Eden區(qū)創(chuàng)建,當(dāng)Eden區(qū)剩余的容量不足以創(chuàng)建新的對象時,將所有的Eden區(qū)存活的對象移動到from區(qū),在下一輪GC時會將from和Eden區(qū)存活的對象全部復(fù)制到to區(qū),將from區(qū)和Eden區(qū)全部清空,將to區(qū)和from進(jìn)行調(diào)換繼續(xù)進(jìn)行垃圾回收。
?2.Full GC(老年代的垃圾回收)
老年代的垃圾回收策略并不是采用復(fù)制算法,而是標(biāo)記整理算法, 因為進(jìn)入老年代的對象相對于新生代而言并不會進(jìn)行頻繁的創(chuàng)建和銷毀,所以使用標(biāo)記整理算法比較適合。Full GC相對于Minor GC要慢很多,所以在JVM調(diào)優(yōu)過程中,很大的一部分工作都是對Full GC的調(diào)節(jié)
3.垃圾回收器
介紹一下垃圾回收器(M)
在講述垃圾回收器之前,我們先說一下垃圾回收器的發(fā)展過程:由于在垃圾回收的過程中要執(zhí)行STW,所有的用戶線程在這個過程中都要暫停線程服務(wù),所以縮短STW的時間是垃圾回收器在完成業(yè)務(wù)需求之后所要追求的目標(biāo).。在剛開始時使用單線程垃圾回收器,隨著程序規(guī)模的不斷擴(kuò)大和程序內(nèi)容的不斷豐富,單線程垃圾回收器無法滿足用戶需求,多線程垃圾回收器應(yīng)運而生,隨著程序的不斷發(fā)展,多線程STW的時間也越來越長,又開始嘗試新的垃圾回收器來縮短STW的時間文章來源:http://www.zghlxwxcb.cn/news/detail-432107.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-432107.html
?主要的垃圾回收器有以下幾種:
- Serial收集器(復(fù)制算法)
新生代單線程收集器,標(biāo)記和清理都是單線程,優(yōu)點是簡單高效。是client級別默認(rèn)的GC方式,可以通過-XX:+UseSerialGC
來強制指定。- Serial Old收集器(標(biāo)記-整理算法)
老年代單線程收集器,Serial收集器的老年代版本。- ParNew收集器(停止-復(fù)制算法)
新生代收集器,可以認(rèn)為是Serial收集器的多線程版本,在多核CPU環(huán)境下有著比Serial更好的表現(xiàn)。- Parallel Scavenge收集器(停止-復(fù)制算法)
并行收集器,追求高吞吐量,高效利用CPU。吞吐量一般為99%, 吞吐量= 用戶線程時間/(用戶線程時間+GC線程時間)。適合后臺應(yīng)用等對交互相應(yīng)要求不高的場景。是server級別默認(rèn)采用的GC方式,可用-XX:+UseParallelGC
來強制指定,用-XX:ParallelGCThreads=4
來指定線程數(shù)。- Parallel Old收集器(停止-復(fù)制算法)
Parallel Scavenge收集器的老年代版本,并行收集器,吞吐量優(yōu)先。- CMS(Concurrent Mark Sweep)收集器(標(biāo)記-清理算法)
高并發(fā)、低停頓,追求最短GC回收停頓時間,cpu占用比較高,響應(yīng)時間快,停頓時間短,多核cpu 追求高響應(yīng)時間的選擇。
到了這里,關(guān)于JVM,關(guān)于JVM基礎(chǔ)的知識,你確定不了解一下嗎?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!