最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

JVM面試總結(jié)(三)

2022-04-27 22:34 作者:吾之利劍  | 我要投稿

????JVM是面試必問的模塊,整個JVM我個人感覺可以分為內(nèi)存模型、類加載機制、gc垃圾回收和性能優(yōu)化四個大塊;

今天主要總結(jié)一下gc(Garbage Collector)垃圾回收機制;

1、為什么要進行垃圾回收

????在C++中,對象所占的內(nèi)存在程序結(jié)束運行之前一直被占用,在明確釋放之前不能分配給其它對象;而在Java中,當沒有對象引用指向原先分配給某個對象 的內(nèi)存時,該內(nèi)存便成為垃圾。

????垃圾回收能自動釋放內(nèi)存空間,減輕編程的負擔,JVM的一個系統(tǒng)級線程會自動釋放該內(nèi)存塊。垃圾回收意味著程序不再需要的對象是"無用信息",這些信息將被丟棄。當一個對 象不再被引用的時候,內(nèi)存回收它占領(lǐng)的空間,以便空間被后來的新對象使用。

????事實上,除了釋放沒用的對象,垃圾回收也可以清除內(nèi)存記錄碎片。由于創(chuàng)建對象和垃圾回收器釋放丟棄對象所占的內(nèi)存空間,內(nèi)存會出現(xiàn)碎片。碎片是分配給對象的內(nèi)存塊之間的空閑內(nèi)存洞。碎片整理將所占用的堆內(nèi)存移到堆的一端,JVM將整理出的內(nèi)存分配給新的對象。

主要總結(jié):

????1、不進行垃圾回收,可能會導致內(nèi)存不夠用。

????2、除了釋放無用的對象,gc也可以清除內(nèi)存中的記錄碎片,進行碎片整理, 將堆內(nèi)存移到堆的另一端,以便JVM將整理出的內(nèi)存分配給新的對象。

????3、現(xiàn)在應用程序所對應的業(yè)務,用戶群體日益強大,沒有g(shù)c無法保證應用程序的正常運行。


2、簡述Java垃圾回收機制

????這個考察對垃圾回收機制整體的了解;一般從以下幾個方面回答:

1、哪些內(nèi)存需要回收;

????Java的內(nèi)存運行區(qū)數(shù)據(jù)中,程序計數(shù)器、虛擬機棧、本地方法棧這3個區(qū)域隨線程而生亡,其棧幀分配多少內(nèi)存基本上是在類結(jié)構(gòu)確定下來時就已知的,所以這3個區(qū)域的內(nèi)存分配和回收都具備確定性,而不需要關(guān)注如何回收:當方法結(jié)束或者線程結(jié)束時,內(nèi)存自然就回收了。

????那么需要垃圾回收的只有Java堆和方法區(qū)。一個接口的多個實現(xiàn)類需要的內(nèi)存可能會不一樣,一個方法所執(zhí)行的不同條件分支所需要的內(nèi)存也可能不一樣,只有處于運行期間,我們才能知道程序究竟會創(chuàng)建哪些對象,創(chuàng)建多少個對象,這部分內(nèi)存的分配和回收是動態(tài)的。垃圾收集器所關(guān)注的正是這部分內(nèi)存該如何管理,我們平時所說的內(nèi)存分配與回收也僅僅特指這一部分內(nèi)存。

2、怎么定義垃圾

????JVM的gc工作主要針對的對象是堆內(nèi)存,在做gc工作之前,首先要判定堆內(nèi)存中的對象實例是否為垃圾,通常使用以下兩種算法來定義:

????1、引用計數(shù)算法

????Java在運行時,當有一個地方引用該對象實例,會將這個對象實例加1,引用失效時就減1,JVM在掃描內(nèi)存時,發(fā)現(xiàn)引用計數(shù)值為0的則是垃圾對象,計數(shù)值大于0的則為活躍對象。

????目前垃圾回收算法,沒有采用引用計數(shù)算法,原因是在對象互相引用的情況下,無法判定兩者是否為垃圾對象。

????2、可達性分析算法(根搜索算法)

????當前主流的商用程序語言的內(nèi)存管理子系統(tǒng),都是通過可達性分析(Reachability Analysis)算法來判定對象是否存活的。這個算法的基本思路就是通過一系列稱為“gc Roots”的根對象作為起始節(jié)點集,從這些節(jié)點開始,根據(jù)引用關(guān)系向下搜索,搜索過程所走過的路徑稱為“引用鏈”(Reference Chain),如果某個對象到gc Roots間沒有任何引用鏈相連,或者用圖論的話來說就是從gc Roots到這個對象不可達時,則表明該對象不可能再被使用。

在Java里面,固定可作為gc Roots的對象包括以下幾種:

????1、在虛擬機棧(棧幀中的本地變量表)中引用的對象,例如各個線程被調(diào)用的方法堆棧中使用到的參數(shù)、局部變量、臨時變量等?.

????2、在方法區(qū)中類靜態(tài)屬性引用的對象,例如Java類的引用類型靜態(tài)變量。

????3、在方法區(qū)中常量引用的對象,例如字符串常量池里的引用。

????4、在本地方法棧中JNI(Native方法)引用的對象。

????5、Java虛擬機內(nèi)部的引用,如基本數(shù)據(jù)類型對應的Class對象,一些常駐的異常對象(比如NullPointExcepiton、OutOfMemoryError)等,還有系統(tǒng)類加載器。

????6、所有被同步鎖(synchronized關(guān)鍵字)持有的對象。

????7、反映JVM內(nèi)部情況的JMXBean、JVMTI中注冊的回調(diào)、本地代碼緩存等判定一個類型是否屬于“不再被使用的判定一個類型是否屬于“不再被使用的類”的條件就比較苛刻了,需要同時滿足下面三個條件:

????1、該類所有的實例都已經(jīng)被回收,也就是Java堆中不存在該類及其任何派生子類的實例。

????2、加載該類的類加載器已經(jīng)被回收,這個條件除非是經(jīng)過精心設計的可替換類加載器的場景,如OSGi、JSP的重加載等,否則通常是很難達成的。

????3、該類對應的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。??

?????3、怎么回收垃圾

????即就是垃圾回收算法。

3、JVM的引用類型有哪些?

????1、強引用(StrongReference):最傳統(tǒng)的“引用”的定義,是指在程序代碼之中普遍存在的引用賦值,即類似“Object obj = new Object()”這種引用關(guān)系。無論任何情況下,只要強引用關(guān)系還存在,垃圾收集器就永遠不會回收掉被引用的對象。

????2、軟引用(SoftReference):在系統(tǒng)將要發(fā)生內(nèi)存溢出之前,將會把這些對象列入回收范圍之中進行第二次回收。如果這次回收后還沒有足夠的內(nèi)存,才會拋出內(nèi)存流出異常。

????3、弱引用(WeakReference):被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾收集之前。當垃圾收集器工作時,無論內(nèi)存空間是否足夠,都會回收掉被弱引用關(guān)聯(lián)的對象。

????4、虛引用(PhantomReference):一個對象是否有虛引用的存在,完全不會對其生存時間構(gòu)成影響,也無法通過虛引用來獲得一個對象的實例。為一個對象設置虛引用關(guān)聯(lián)的唯一目的就是能在這個對象被收集器回收時收到一個系統(tǒng)通知。

4、垃圾回收算法(如何判斷對象是否存活)

1、標記-清除(Mark-Sweep)

????標記-清除算法,和字面意思一樣,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統(tǒng)一回收掉所有被標記的對象。之所以說它是最基礎的收集算法,是因為后續(xù)的收集算法都是基于這種思路并對其缺點進行改進而得到的。

? ?它的主要缺點有兩個: ? ? ?

?1、效率問題:標記和清除過程的效率都不高 ? ??

? 2、空間問題:標記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導致,碎片過多會導致大對象無法分配到足夠的連續(xù)內(nèi)存,從而不得不提前觸發(fā)gc,甚至Stop The World。


2、標記-復制算法

????標記-復制算法可以解決效率問題。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的內(nèi)存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。 ? ?這樣使得每次都是對其中的一塊進行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可,實現(xiàn)簡單,運行高效(From Survivor和To Survivor使用的就是復制算法。老年代不使用這種算法)。

? 它的主要缺點有兩個: ? ? ?

?1、效率問題:在對象存活率較高時,復制操作次數(shù)多,效率降低; ? ? ??

?2、空間問題:內(nèi)存縮小了一半;需要額外空間做分配擔保(老年代)

?

3、標記-整理算法

????標記-復制算法在對象存活率較高時就要進行較多的復制操作,效率將會降低。更關(guān)鍵的是,如果不想浪費50%的空間,就需要有額外的空間進行分配擔保,以應對被使用的內(nèi)存中所有對象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。 針對老年代對象的存亡特征,出現(xiàn)了有針對性的“標記-整理”(Mark-Compact)算法,其中的標記過程仍然與“標記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向內(nèi)存空間一端移動,然后直接清理掉邊界以外的內(nèi)存。

4、分代回收

????分代的垃圾回收策略,是基于這樣一個事實:不同的對象的生命周期(對象熬過垃圾收集過程的次數(shù))是不一樣的。因此,不同生命周期的對象可以采取不同的回收算法,以便提高回收效率目前JVM常用回收算法就是分代回收,年輕代以復制算法為主,老年代以標記整理算法為主。

1、新生代(Young Generation)

????1、所有新生成的對象首先都是放在新生代的。新生代的目標就是盡可能快速的收集掉那些生命周期短的對象。

????2、新生代內(nèi)存按照8: 1: 1的比例分為一個Eden區(qū)和兩個survivor(survivor0,survivor1)區(qū)。一個Eden區(qū),兩個 Survivor區(qū)(一般而言)。大部分對象在Eden區(qū)中生成。回收時先將Eden區(qū)存活對象復制到一個survivor0區(qū),然后清空Eden區(qū),當這個survivor0區(qū)也存放滿了時,則將eden區(qū)和survivor0區(qū)存活對象復制到另一個survivor1區(qū),然后清空Eden和這個survivor0區(qū),此時survivor0區(qū)是空的,然后將survivor0區(qū)和survivor1區(qū)交換,即保持survivor1區(qū)為空, 如此往復。

????3、當survivor1區(qū)不足以存放 Eden和survivor0的存活對象時,就將存活對象直接存放到老年代。若是老年代也滿了就會觸發(fā)一次Full gc,也就是新生代、老年代都進行回收

????4、新生代發(fā)生的gc也叫做Minor gc,Minorgc發(fā)生頻率比較高(不一定等Eden區(qū)滿了才觸發(fā))

2、年老代(Old Generation)

????1.在年輕代中經(jīng)歷了N次垃圾回收后仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。

????2.內(nèi)存比新生代也大很多(大概比例是1:2),當老年代內(nèi)存滿時觸發(fā)Major gc即Full gc,F(xiàn)ull gc發(fā)生頻率比較低,老年代對象存活時間比較長,存活率標記高。

3、持久代(Permanent Generation)

????用于存放靜態(tài)文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響,但是有些應用可能動態(tài)生成或者調(diào)用一些class,例如Hibernate 等,在這種時候需要設置一個比較大的持久代空間來存放這些運行過程中新增的類。一般至少會把Java堆劃分為新生代(Young Generation)和老年代(Old Generation)兩個區(qū)域


5、垃圾收集器

各種回收器,各自優(yōu)缺點,重點CMS、G1

????1、 Serial收集器,串行收集器是最古老,最穩(wěn)定以及效率高的收集器,但可能會產(chǎn)生較長的停頓,只使用一個線程去回收。

參數(shù)設置 ? -XX:+UseSerialgc 新生代和老年代都用串行收集器

????2、 ParNew收集器,ParNew收集器其實就是Serial收集器的多線程版本。

????3、 Parallel收集器,Parallel Scavenge收集器類似ParNew收集器,Parallel收集器更關(guān)注系統(tǒng)的吞吐量。

????4、 Parallel Old收集器,Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程“標記-整理”算法

????5、 CMS收集器

????收集器是一種以獲取最短回收停頓時間為目標的收集器。目前很大一部分的 Java 應用集中在互聯(lián)網(wǎng)站或者 B/S 系統(tǒng)的服務端上,這類應用尤其重視服務的響應速度,希望系統(tǒng)停頓時間最短,以給用戶帶來較好的體驗。CMS 收集器就非常符合這類應用的需求。

????從名字(包含“Mark Sweep”)上就可以看出,CMS 收集器是基于“標記—清除”算法實現(xiàn)的,它的運作過程相對于前面幾種收集器來說更復雜一些,整個過程分為 4 個步驟,包括:

????1、初始標記-短暫,僅僅只是標記一下 gc Roots 能直接關(guān)聯(lián)到的對象,速度很快。

????2、并發(fā)標記-和用戶的應用程序同時進行,進行 gc Roots 追蹤的過程,標記從 gcRoots 開始關(guān)聯(lián)的所有對象開3始遍歷整個可達分析路徑的對象。這個時間比較長,所以采用并發(fā)處理(垃圾回收器線程和用戶線程同時工作)。'

????3、重新標記-短暫,為了修正并發(fā)標記期間因用戶程序繼續(xù)運作而導致標記產(chǎn)生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比并發(fā)標記的時間短。

????4、并發(fā)清除由于整個過程中耗時最長的并發(fā)標記和并發(fā)清除過程收集器線程都可以與用戶線程一起工作,所以,從總體上來說,CMS 收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的。

參數(shù)-XX:+UseConcMarkSweepgc ,表示新生代使用 ParNew,老年代的用 CMS

6、 G1收集器

????G1(Garbage First)是一款主要面向服務端應用的垃圾收集器, 在JDK1.7版本正式啟用,是JDK 9以后的默認垃圾收集器。G1宣告取代ParallelScavenge加Parallel Old組合,成為服務端模式下的默認垃圾收集器,而CMS則淪落至被聲明為不推薦使用(Deprecate)的收集器。

????G1仍然保留新生代和老年代的概念,但新生代和老年代不再是固定的了,它們都是一系列區(qū)域(不需要連續(xù))的動態(tài)集合。G1收集器之所以能建立可預測的停頓時間模型,是因為它將Region作為單次回收的最小單元,即每次收集到的內(nèi)存空間都是Region大小的整數(shù)倍,這樣可以有計劃地避免在整個Java堆中進行全區(qū)域的垃圾收集。更具體的處理思路是讓G1收集器去跟蹤各個Region里面的垃圾堆積的“價值”大小,價值即回收所獲得的空間大小以及回收所需時間的經(jīng)驗值,然后在后臺維護一個優(yōu)先級列表,每次根據(jù)用戶設定允許的收集停頓時間(使用參數(shù)-XX:MaxgcPauseMillis指定,默認值是200毫秒),優(yōu)先處理回收價值收益最大的那些Region,這也就是“Garbage First”名字的由來。這種使用Region劃分內(nèi)存空間,以及具有優(yōu)先級的區(qū)域回收方式,保證了G1收集器在有限的時間內(nèi)獲取盡可能高的收集效率。

參數(shù)設置 開啟參數(shù)-XX:+UseG1gc 分區(qū)大小-XX:+G1HeapRegionSize

6、JVM中一次完整的gc流程是怎樣的

????1、大對象直接進入到老年代?。

????2、小對象先在eden區(qū)分配內(nèi)存,當eden滿了后,觸發(fā)一次Minor gc,清理eden區(qū)域。 ????????3、存活下來的對象進入到survivor區(qū)域,年齡+1 4、當年齡>15(默認)時進入到老年代,當老年代滿了后觸發(fā)一次Full gc。

7、Full gc會導致什么?

????Full gc的時候除 gc線程外的所有用戶線程處于暫停狀態(tài),也就是不會有響應了。

????一般Full gc速度很快,毫秒級的,用戶無感知。除非內(nèi)存特別大上百G的,或者Full gc也無法收集到足夠內(nèi)存導致一直Full gc,應用的外在表現(xiàn)就是程序卡死了。

8、什么時候JVM觸發(fā)gc,如何減少Fullgc的次數(shù)

????當 Eden 區(qū)的空間耗盡時 Java 虛擬機便會觸發(fā)一次 Minor gc 來收集新生代的垃圾,存活下來的對象,則會被送到 Survivor 區(qū),簡單說就是當新生代的Eden區(qū)滿的時候觸發(fā) Minor gc。

????serial gc 中,老年代內(nèi)存剩余已經(jīng)小于之前年輕代晉升老年代的平均大小,則進行 Full gc。而在 CMS 等并發(fā)收集器中則是每隔一段時間檢查一下老年代內(nèi)存的使用量,超過一定比例時進行 Full gc 回收。

可以采用以下措施來減少Full gc的次數(shù):

  1. 增加方法區(qū)的空間;

  2. 增加老年代的空間;

  3. 減少新生代的空間;

  4. 禁止使用System.gc()方法;

  5. 使用標記-整理算法,盡量保持較大的連續(xù)內(nèi)存空間;

  6. 排查代碼中無用的大對象。

注意:

gc的回收時間是不確定的,即使你顯示的調(diào)用的System.gc()。因為和線程優(yōu)先級有關(guān)

使用了finalize()方法之后,gc是在這個方法執(zhí)行之后的下一次進行垃圾的回收。

9、擴展

1、JVM查看gc命令

2、如果頻繁老年代回收怎么分析解決?

3、重點考察點是分代回收算法,CMS收集器和G1收集器

參數(shù):

-Xms 初始堆大小。如:-Xms256m

-Xmx 最大堆大小。如:-Xmx512m

-Xmn 新生代大小。通常為 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 個 Survivor 空間。實際可用空間為 = Eden + 1 個 Survivor,即 90%

-Xss JDK1.5+ 每個線程堆棧大小為 1M,一般來說如果棧不是很深的話, 1M 是絕對夠用了的。-XX:NewRatio 新生代與老年代的比例,如 –XX:NewRatio=2,則新生代占整個堆空間的1/3,老年代占2/3

-XX:SurvivorRatio 新生代中 Eden 與 Survivor 的比值。默認值為 8。即 Eden 占新生代空間的 8/10,另外兩個 Survivor 各占 1/10

-XX:PermSize 永久代(方法區(qū))的初始大小

-XX:MaxPermSize 永久代(方法區(qū))的最大值

-XX:+PrintgcDetails 打印 gc 信息

-XX:+HeapDumpOnOutOfMemoryError 讓虛擬機在發(fā)生內(nèi)存溢出時 Dump 出當前的內(nèi)存堆轉(zhuǎn)儲快照,以便分析用



以上內(nèi)容僅供參考,請合理利用搜索引擎!




JVM面試總結(jié)(三)的評論 (共 條)

分享到微博請遵守國家法律
汉阴县| 察雅县| 清新县| 葫芦岛市| 惠水县| 保靖县| 昌乐县| 阳新县| 从江县| 进贤县| 苗栗市| 和政县| 柳州市| 正镶白旗| 施秉县| 玛曲县| 虞城县| 太原市| 西畴县| 兴化市| 双桥区| 洪洞县| 蒙山县| 大化| 洛宁县| 扶风县| 宁德市| 盐边县| 吴桥县| 贵溪市| 利川市| 贞丰县| 本溪| 昆山市| 景德镇市| 甘洛县| 聂荣县| 宽城| 沽源县| 霍州市| 多伦县|