經驗整理-1-JVM-內存調優參數收集器等介紹-100-@

-------------------new1.8-------------《深入理解Java虛擬機:JVM高級特性與最佳實踐》周志明--

?講一下拉圾回收過程原理?

示例1,拉圾回收過程原理:(執行過程如上圖根據執行日誌順序)
第一步,新生代內存分配區域,eden空間100%滿後,無法分配內存。
第二步,gc開始回收、保留一部分剩餘存活對象存放survivor(假設to區),利用copy複製算法,把from的剩餘存活對象轉到to區,始終保持一個(from)survivor爲empty。併爲轉移對象age+1.
第三步,之間一直循環上述步驟,當age滿足很大的時候觸發老年代gc回收保存到old 老年代區。(老年這裏還不是full gc,還沒滿)
第四步,由於不斷會有對象進入老年代,老年代內存會一直增大加上新生代達到臨界值內存最大值。觸發full gc進行整體回收。

疑問點:那你沒有說明元空間啊?元空間幹啥的呢?
元空間存放:class文件、靜態對象、屬性等。而且在永久代的時候默認大小256m。但是在元空間jdk8後,它是jvm根據需要動態加載大小(可能是上限是全部剩餘內存,可拓展,節省了內存空間)。
 

每次存活對象的判斷原理:通過可達性分析去判斷對象是被引用,不被引用的被第一次標記(當然CMS和G1可能是2步才標記),然後會判斷對象,如果重寫了finalize(),會被放入F-Query隊列,然後執行finalize(),重新恢復引用鏈。finalize執行過會有標記,如果下次回收,不會再執行finalize。

?新生代與老年代的拉圾回收機制?

jdk8環境下
底層實現:新生代採用複製算法,老年代採用標記-清除算法(cms是,g1是標記-整理)。
默認使用 Parallel Scavenge(新生代)+ Serial Old(老年代).
一般我們使用:
ParNew(新生代)+ CMS(老年代(用基於"標記-整理"算法的Serial Old作爲替補-,解決這個問題:當產生大量的內存碎片,使剩餘內存不能滿足程序時報錯就採用Serial Old))
如果要控制吞吐量體驗,只用:G1(新生代+老年代)

注:
parallel scavenge 與parnew 區別-----parallel scavenge 可以設置最大gc停頓時間(-XX:MaxGCPauseMills)以及gc執行時間佔比(-XX:GCTimeRatio)
Serial Old與CMS區別-----G1獨立管理整個GC堆,但是還是保留了分代的概念;G1能設置停頓時間
ParNew+CMS 與G1區別-----G1獨立管理整個GC堆,但是還是保留了分代的概念;G1能設置停頓時間;G1多了mixedGC,基本不會fullGC

注:
ParNew原理---多線程進行復制算法
CMS原理---“標記-清除”算法,
初始標記:暫停所有的其他線程,並記錄下直接與root相連的對象,速度很快
併發標記:開啓GC線程和用戶線程,並行去記錄可達對象,因爲並行,所以可能漏掉可達對象
重新標記:修正併發標記期間因爲用戶程序繼續運行而導致標記產生變動的那一部分漏掉的可達對象的標記記錄
併發清除:開啓線程,並行去清掃標記的區域
G1
原理---“標記-整理”算法,
初始標記:暫停所有的其他線程,並記錄下直接與root相連的對象,速度很快
併發標記:開啓GC線程和用戶線程,並行去記錄可達對象,因爲並行,所以可能漏掉可達對象

最終標記:修正併發標記期間因爲用戶程序繼續運行而導致標記產生變動的那一部分漏掉的可達對象的標記記錄
篩選回收:開啓線程,並行去清掃標記的區域,並且根據設置的GC停頓時間,優先選擇回收價值最大的Region

JVM調優主要就是調整下面兩個指標
停頓時間:垃圾收集器做垃圾回收中斷應用執行的時間。-XX:MaxGCPauseMillis
吞吐量:非垃圾收集的時間和總時間的佔比:1/(1+n),吞吐量爲1-1/(1+n)。-XX:GCTimeRatio=n(GC停頓時間短,吞吐量就高)


GC調優步驟(調優至99%吞吐量)

1.開啓打印GC日誌
-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCDateStamps-Xloggc:./gc.log
Tomcat可以直接修改JAVA_OPTS變量
2.如果系統慢或報錯,下載日誌文件,一般我們用gceasy分析,會有一些建議性的提示會高效很多。
3.導入gceasy,查看分析日誌得到關鍵性指標:比如吞吐量、GC停頓時間
3.分析GC原因,調優JVM參數,比如:
.OutOfMemoryError,見下面三種:
1.(堆)溢出,手動設大 JVM Heap(堆)的大小。
2.持久代(元空間)溢出,手動設大 MaxPermSize 大小
3.棧溢出,1MB 是足夠的,一般256K就夠了,要想着從代碼層優化:不要遞歸的層次過多

.比如頻繁fullGC:考慮增加老年代比例(調小新生代比例參數)
.還有幾種如初始堆內存設到最大推內存一樣,見下面一個問題JVM調優明細

?你平時是怎麼進行tomcat調優的?(新生代使用並行收集器ParNew(無碎片,停頓不明顯,吐量高),老年代使用並行標記整理收集器CMS )

一、Tomcat的自身調優
1.緩存優化,採用動靜分離節約Tomcat 的性能:採用Nginx+Tomcat 實現動靜分離,讓 Tomcat 只負責 jsp 文件的解析工作,Nginx負責靜態資源的訪問(靜態頁面緩存到Nginx
2.調整 Tomcat 的線程池:比如線程池中最大的線程數量(默認值爲150,改爲400),線程池中允許空閒的線程數量等(空閒改成4個)
3.調整 Tomcat 的連接器:比如,指定Tomcat連接器所使用的執行器(線程池) 是我們前面配好的線程池;網絡連接超時設置爲20秒,設置不允許反查域名關閉客戶端dns查詢,提高處理能力
4.修改 Tomcat 的運行模式:APR運行模式,APR是從操作系統級別解決異步 IO 問題(提升 Tomcat 對靜態文件的處理性能,當然也可以採用動靜分離。)
5.禁用 AJP 連接器:使用 Nginx+Tomca t的架構,所以用不着 AJP 協議,所以把AJP連接器禁用。系統安裝 Apr 庫(性能最優),默認就啓用的 Apr 協議,不然會使用 bio 方式
6.使用tomcat集羣,配合nginx做集羣
二、JVM的調優  (考慮1.停頓時長:2.吞吐量:3.頻繁率)(默認Edem : from : to = 8 : 1 : 1,新生代 與老年代 爲 1:2
Tomcat 是運行在 JVM 上的,所以對 JVM 的調優也是非常有必要的。(修改啓動腳本catalina.sh加上:JAVA_OPTS='-server -Xms1024m -Xmx1024m...'
例如:假如4核8G,指定---新生代使用並行回收器ParNew(無碎片,停頓不明顯),老年代使用並行標記整理回收器CMS (選型原因?如下)
1.最大可用推內存,從默認的 256M增大到4G  (減少GC次數,少停頓吞吐量就越高
2.初始堆內存設到最大推內存一樣,  (避免收縮堆內存大小,影響效率,---防止垃圾收集器在最小、最大之間收縮堆而產生額外的時間
3.-Xss 每個線程堆棧的大小 一般情況下256K是足夠了,調小可以提升併發線程數(代碼不要遞歸的層次過多)
4.設置持久代初始值和最大值一樣。
5.大對象直接進入老年代(設置閥值)
6.設置對象進入老年代的年齡
7.吞吐量優先設置,(更像是G1裏的,但老年代的CMS也有一個擇中辦法:兼顧停頓時間不能太長和空間碎片定次整理
8將新對象預留在新生代,
其他版本:

?三種JVM內存溢出怎麼解決?

1.(堆)溢出,手動設大 JVM Heap(堆)的大小。
2.持久代(元空間)溢出,手動設大 MaxPermSize 大小
3.棧溢出,1MB 是足夠的,一般256K就夠了,要想着從代碼層優化:不要遞歸的層次過多



 

調優總結

初始堆值和最大堆內存內存越大,吞吐量就越高
最好使用
並行收集器,因爲並行收集器速度比串行吞吐量高,速度快
設置堆內存
新生代的比例和老年代的比例最好爲1:2或者1:3
減少GC對老年代的回收。

 

GC是在什麼時候,對什麼東西,做了什麼事情?

一、什麼時候
發生GC分幾種情況:
新生代minor gc----Eden區域滿了,或者新創建的對象大小 > Eden所剩空間
新生代major gc----如下
新生代Full GC----新生代直接晉升到老年代的大對象超過了老年代的剩餘空間或超過設定年齡生存次數,Major GC發生,由於minor 剛纔也發生了,所以全回收一次,可以把此當成fullGC,  (System.gc()也可以,但一般會設置來禁止這麼幹)
Mixed GC----收集整個young gen以及部分old gen的GC。只有G1有這個模式

二、對什麼東西 
從root搜索不到,而且經過第一次標記、清理後,仍然沒有復活的對象。 
    分析:我期待的答案。但是的確很少面試者會回答到這一點,所以在我心中回答道第3點我就給全部分數。 
三、做了什麼事情(直接講原理就好)
新生代用多線程做了並行複製算法,通常用parnew,比如對象GC之後先放入from,下次GC時會把from裏存活的全複製到to區,清空from,複製算法好處----無碎片,但犧牲一半空間。
老年代做的是併發標記-清除,通常用CMS,標記-清除算法好處--節省空間,但有碎片。 


調優:
第一次調優,設置Metaspace大小:增大元空間大小-XX:MetaspaceSize=64M-XX:MaxMetaspaceSize=64M
第二次調優,添加吞吐量和停頓時間參數:-XX:GCTimeRatio=99 -XX:MaxGCPauseMillis=10
GC常用參數

堆棧設置
-Xss:每個線程的棧大小
-Xms:初始堆大小,默認物理內存的1/64
-Xmx:最大堆大小,默認物理內存的1/4
-Xmn:新生代大小
-XX:NewSize:設置新生代初始大小
-XX:NewRatio:默認2表示新生代佔年老代的1/2,佔整個堆內存的1/3。
-XX:SurvivorRatio:默認8表示一個survivor區佔用1/8的Eden內存,即1/10的新生代內存。
-XX:MetaspaceSize:設置元空間大小
-XX:MaxMetaspaceSize:設置元空間最大允許大小,默認不受限制,JVM Metaspace會進行動態擴展。
垃圾回收統計信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
收集器設置
-XX:+UseParNewGC:在新生代使用並行收集器
並行收集器設置
-XX:ParallelGCThreads:設置並行收集器收集時使用的CPU數。並行收集線程數。
-XX:MaxGCPauseMillis:設置並行收集最大暫停時間
-XX:GCTimeRatio:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)
CMS收集器設置
-XX:+UseConcMarkSweepGC:設置CMS併發收集器
-XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU情況。
-XX:ParallelGCThreads:設置併發收集器新生代收集方式爲並行收集時,使用的CPU數。並行收集線程數。
-XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS垃圾回收後,進行一次內存壓縮
-XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收
-XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU情況
-XX:ParallelCMSThreads:設定CMS的線程數量
-XX:CMSInitiatingOccupancyFraction:設置CMS收集器在老年代空間被使用多少後觸發
-XX:+UseCMSCompactAtFullCollection:設置CMS收集器在完成垃圾收集後是否要進行一次內存碎片的整理
G1收集器設置
-XX:+UseG1GC:使用G1收集器
-XX:ParallelGCThreads:指定GC工作的線程數量
-XX:G1HeapRegionSize:指定分區大小(1MB~32MB,且必須是2的冪),默認將整堆劃分爲2048個分區
-XX:GCTimeRatio:吞吐量大小,0-100的整數(默認9),值爲n則系統將花費不超過1/(1+n)的時間用於垃圾收集
-XX:MaxGCPauseMillis:目標暫停時間(默認200ms)
-XX:G1NewSizePercent:新生代內存初始空間(默認整堆5%)
-XX:G1MaxNewSizePercent:新生代內存最大空間
-XX:TargetSurvivorRatio:Survivor填充容量(默認50%)
-XX:MaxTenuringThreshold:最大任期閾值(默認15)
-XX:InitiatingHeapOccupancyPercen:老年代佔用空間超過整堆比IHOP閾值(默認45%),超過則執行混合收集
-XX:G1HeapWastePercent:堆廢物百分比(默認5%)
-XX:G1MixedGCCountTarget:參數混合週期的最大總次數(默認8)

-------------------Old-------------  

巧記:

我的記憶方式如下:
近身類
1)-Xms*M          ,ms看做的memory size(初始堆內存大小)的縮寫,就是初始堆大小
2)-Xmx*M         , Maximum heap memory(最大堆內存),x代表最大,所以就最大堆內存
3)-Xss*K         , ss就是stack size(棧大小)的縮寫,所以是用來代表線程棧的大小
4)-Xmn*M           ,n代表是memory new(內存新生代/新生代堆最大可用值),所以mn就是用來指定新生代的堆內存空間大小

等於符號類
1)-XX:PermSize=*M        設置Persistent Size(持久區大小),在jdk 8中已經被metaspace取代
2)-XX:MaxPermSize=*M          設置Maximum Persistent Size(最大持久區大小),在jdk 8中已經被metaspace取代
3)-XX:SurvivorRatio=1:1       用來設置新生代中eden空間和from(或to)空間的比例.
固定開關:
-XX:+PrintGC         每次觸發GC的時候打印相關日誌
-XX:+UseSerialGC         串行回收
-XX:+PrintGCDetails      更詳細的GC日誌


?說說JVM內存模型及作用?

一、---------五大內存模塊---------(1.8版本以前方法區是在堆裏面的,所以以前學的常量那些都在堆裏---1.8多出來一個方法區,把常量等從堆裏分出來)
方法區:常量、靜態變量、類信息、Jit (生效時間:隨JVM啓動程序開始運行加載所有類信息開始,失效時間:JVM程序停止運行)--(原空間)
堆(大範圍):存對象的實例(實例裏包含有成員變量) (生效時間:隨類被New創建實例信息開始,失效時間:實例外)--還包含方法裏new的實例本身(對象引用是局部變量)
棧(小範圍臨時客棧):普通方法及內部局部變量(生效時間:隨方法被調用開始,失效時間:方法外)
本地方法棧:native修飾的方法, Java 通過調用這個接口從而調用到 C/C++ 方法
程序計數器: 是用於存放下一條指令所在單元的地址的地方。---存儲 每個指 令單元的地址,線程阻塞恢復運行時,能立馬通過當前這個線程存在於程序計數器的指令地址,找到對應的字節碼指令繼續執行

?說說對垃圾回收機制的瞭解

     什麼是拉圾回收:一個內存對象的生命週期超出了程序需要它的時間長度,也就是說沒有人引用它了,它還“遊離”在內存中,會浪費資源,我們需要對它清理就是拉圾回收。
    垃圾回收簡要過程總結:所有沒有再被引用的對象,都會被第一次標記;如果重寫了finalize(),會被放入F-Query隊列,然後執行finalize(),執行finalize()時重新與GC Roots引用鏈恢復連接的對象,會獲得唯一一次機會恢復成正常的對象,其他不滿足條件的就會在本次回收掉。
   
結合對象的生命週期說一下吧, 拉圾回收機制會根據生命週期長短不一樣,把對象放進不同的內存區.
第一步,首先一個對象,會在JVM內存的eden區被創建,分配內存區域,eden空間100%滿後,無法分配內存,會觸發GC回收。
第二步,gc回收時,保留一部分剩餘存活對象存入survivor(假設to區),同時,利用copy複製算法,把from的剩餘存活對象也轉到to區,始終保持一個(from=s0)survivor爲empty。併爲轉移對象age+1.
第三步,之後一直循環上述步驟,當age滿足很大的時候觸發老年代gc回收保存到old 老年代區。(老年這裏還不是full gc,還沒滿),如果超過了設置的放入老年代的對象大小閥值,也會直接進入老年代。
第四步,由於不斷會有對象進入老年代,老年代內存會一直增大加上新生代達到臨界值內存最大值。觸發full gc進行整體回收。

疑問點:那你沒有說明元空間啊?元空間幹啥的呢?
元空間存放:class文件、靜態對象、屬性等。而且在永久代的時候默認大小256m,元空間是可以動態拓展的

 

?年輕代和老年代分別選擇什麼選擇器,分別介紹一下優點?

新生代使用並行收集器ParNew(無碎片,停頓不明顯,吐量高),老年代使用並行標記整理收集器CMS
一、並行回收器:在串行回收器基礎上做了改進,他可以使用多個線程同時進行垃
圾回收,基於複製算法,無碎片,停頓不明顯

二、標記整理回收器:整個收集過程大致分爲4個步驟:
①.初始標記(CMS initial mark)  標記出GC ROOTS能直接關聯到的對象  ,停頓時間一般
②.併發標記(CMS concurrenr mark) 進行GC ROOTS 根搜索算法階段(可達性分析),,會判定對象是否存活,不停頓,耗時長
③.重新標記(CMS remark) 修正併發標記期間,因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄  ,停頓時間稍長
.併發清除(CMS concurrent sweep) 併發清除.,不停頓,耗時長
    老年代使用併發標記清理收集器CMS選型原因:CMS垃圾收集器提供了一個可配置的參數,即-XX:+UseCMSCompactAtFullCollection開關參數,用於在“享受”完Full GC服務之後額外免費贈送一個碎片整理的過程(會造成停頓時間增長),空間碎片問題沒有了,但停頓時間不得不變長了,JVM設計者們還提供了另外一個參數 -XX:CMSFullGCsBeforeCompaction,這個參數用於設置在執行多少次不壓縮的Full GC後,跟着來一次帶壓縮的(擇中的辦法:兼顧停頓時間不能太長和空間碎片定次整理

-------------------類加載、字節碼相關-------------

?類的生命週期?

巧記:加驗,準解,初使,卸

加載、驗證、準備、解析、初始化、使用、卸載

 

Java 類加載過程?(JVM加載class文件的原理機制)

類的加載指的是將類的.class文件中的二進制數據,通過類加載器讀入到內存中,將其數據結構放在方法區內,然後在堆區創建一個java.lang.Class對象,,並且向Java程序員提供了訪問方法區內的數據結構的接口

啓動類加載器有三種?

巧記:啓動擴展應用

啓動類加載器 Bootstrap ClassLoader  ----加載JDK安裝目錄\jre\lib下的類庫
擴展類加載器  Extension ClassLoader  ----加載JDK安裝目錄\jre\lib\ext下的類庫
應用程序加載器(系統類加載器)Application ClassLoader  ----加載用戶類路徑(ClassPath)所指定的類

附:用戶自定義加載器(User ClassLoader):負責加載用戶自定義路徑下的類包

雙親委派模型是什麼?怎麼破壞雙親委派模型?

雙親委派機制是:一個類加載器接收到加載類的請求,它首先不會自己加載,而是把請求委派給父加載器加載,每一個層次的加載器都如此,因此,所有的加載請求都應該傳送到頂層的父類加載器,只有當父類加載器反饋自己無法完成加載請求時,子類加載器纔會嘗試自己去加載。

怎麼破壞雙親委派模型:模塊熱部署

 

Java對象創建過程


1.JVM遇到一條新建對象的指令時首先去檢查這個指令的參數是否能在常量池中定義到一個類的符號引用。然後加載這個類(類加載過程在後邊講)
2.爲對象分配內存。一種辦法“指針碰撞”、一種辦法“空閒列表”,最終常用的辦法“本地線程緩衝分配(TLAB)”
3.將除對象頭外的對象內存空間初始化爲0
4.對對象頭進行必要設置

 

 

類加載機制

全盤負責委託機制
當類加載器加載一個類的時候,該類所依賴和引用的類也由這個
類加載器自已載入
(除了顯示的使用另一個ClassLoader外)

雙親委派機制
雙親委派機制得工作過程:
1-類加載器收到類加載的請求;
2-把這個請求委託給父加載器去完成,一直向上委託,直到啓動類加載器;
3-啓動器加載器檢查能不能加載(使用findClass()方法),能就加載(結束);不能加載,則拋出異常,通知子加載器進行加載。
4-重複步驟三; 
以上就是雙親委派機制的原理。


 

 

 

-------------------需要複習的題-------------


-----
21.調優命令
Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo

jps,JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程。
jstat,JVM statistics Monitoring是用於監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
jmap,JVM Memory Map命令用於生成heap dump文件
jhat,JVM Heap Analysis Tool命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內置了一個微型的HTTP/HTML服務器,生成dump的分析結果後,可以在瀏覽器中查看
jstack,用於生成java虛擬機當前時刻的線程快照。
jinfo,JVM Configuration info 這個命令作用是實時查看和調整虛擬機運行參數。
-----
22.調優工具
常用調優工具分爲兩類,jdk自帶監控工具:jconsole和jvisualvm,第三方有:MAT(Memory Analyzer Tool)、GChisto。

jconsole,對JVM中內存,線程和類等的監控
jvisualvm(裝visualGC+jconsole),jdk自帶全能工具,分析程序死鎖,(監視標籤裏還能導出正在監控的)內存dump、線程dump;(用它的VisualGc插件來)監控內存變化、GC變化等。還能導入生產的堆dump文件(dump文件裏能看到類實例對象的大小佔比找出大對象)
GCeasy,一款專業分析gc日誌的工具,能查內存大小分配比例,吞吐量,最大最小停頓時間,普通GC和fullGC的GC耗時時長和,查找內存泄漏
-----
2.JVM的主要組成部分及其作用?

類加載器 ClassLoader:Java代碼 -----> 字節碼 的編譯過程

運行時數據區:把上一步編譯得到的字節碼加載到內存中

執行引擎:命令解析器,解析上一步加載而來的字節碼,翻譯成爲系統指令,交由CPU執行

本地庫接口 Native Interface:諸如IO之類的由其他語言寫成的本地庫接口
-----
3.JVM運行時數據區包含哪些?

程序計數器:行號指示器,通過改變該值,以選取下一步的指令

Java虛擬機棧:局部變量、方法出口等,爲JVM服務

本地方法棧:局部變量、方法出口等,爲本地Native方法服務

堆區:內存最大的一塊,所有的對象實例都在這裏分配內存

方法區:常量、靜態變量等
-------
8.堆棧的區別?

棧內存存儲的是局部變量,堆內存存儲的是實體;

棧內存的更新速度要快於堆內存,因爲局部變量的生命週期很短;

棧內存存放的變量生命週期一旦結束就會被釋放,而堆內存存放的實體會被垃圾回收機制不定時的回收。(所以拉圾回收只回收堆和方法區)
-------
JVM內存模型的相關知識瞭解多少,比如重排序,內存屏障,happen-before,主內存,工作 內存等。
內存屏障:爲了保障執行順序和可見性的一條cpu指令 
重排序:爲了提高性能,編譯器和處理器會對執行進行重拍 
happen-before:操作間執行的順序關係。有些操作先發生。 
主內存:共享變量存儲的區域即是主內存 
工作內存:每個線程copy的本地內存,存儲了該線程以讀/寫共享變量的副本 
-------
講講JAVA的反射機制。
Java程序在運行狀態可以動態的獲取類的所有屬性和方法,並實例化該類,調用方法的功能 

-------

-------

-------

-------

-------

-------

-------

-------

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章