【進階JVM高手之路】32個Java虛擬機知識點快速梳理!

本文來源:《從零開始帶你成爲JVM實戰高手》java

第二週答疑彙總web

============================spring

你們好,我是救火隊隊長。不知不覺,《JVM實戰高手》專欄已經開始兩週了,每篇文章,你們都有各類各樣的問題。數據庫

在解答這些問題的過程當中,我也時不時會受到新的啓發,就是這個技術點,我或許應該這麼講,你們能聽得更加明白一些。tomcat

經過對你們的答疑,反過來讓我繼續打磨文章,這事兒還挺有意義微信

下面將你們上週提出的問題,作了一個小彙總:網絡

問題
併發

既然棧幀存放了方法對應的局部變量的數據,也包括了方法執行的其它相關信息。jvm

那爲何不把程序計算器那塊記錄執行的狀況,也放在各個方法本身的棧幀裏,而是要單獨列一個程序計數區去存儲呢?jsp

答:這就是JVM設計者的設計思想了,由於程序計數器針對的是代碼指令的執行,Java虛擬棧針對的是放方法的數據,一個是指令,一個是數據,分開設計

問題

思考題回答:什麼狀況下一個類會被回收?

  • 首先該類的全部實例(堆中)都已經被回收;

  • 其次該類的ClassLoader已經被回收;

  • 最後,對該類對應的Class對象沒有任何引用。知足上面三個條件就能夠回收該類了。

答:正解

問題

方法執行完後, 棧幀立馬被出棧, 那該棧幀中的變量等數據是立馬就被回收掉嗎?仍是須要等垃圾回收線程掃描到再回收掉?

答:出棧就沒了

問題

若是把public static int flushInterval = Configuration.getInt("xxx");中的static去掉, 那後面的getInt是在何時執行的呢 ?

我本身測試了一下,好像是在構造方法以前執行的, 不明白這個到底屬於什麼階段?

答:這是屬於類的對象實例初始化的階段

問題

雙親委派模型設計的出發點很重要,文章漏了。對於任意一個類,都須要由加載它的類加載器和這個類自己一同確立其在Java虛擬機中的惟一性,每個類加載器,都擁有一個獨立的類名稱空間。

也就是說,判斷2個類是否「相等」,只有在這2個類是由同一個類加載器加載的前提下才有意義

不然即便這2個類來源於同一個Class文件,被同一個虛擬機加載,只要加載它們的類加載器不一樣,這2個類一定不相等。

基於雙親委派模型設計,那麼Java中基礎的類,Object相似Object類重複屢次的問題就不會存在了

由於通過層層傳遞,加載請求最終都會被Bootstrap ClassLoader所響應。加載的Object類也會只有一個

不然若是用戶本身編寫了一個java.lang.Object類,並把它放到了ClassPath中,會出現不少個Object類,這樣Java類型體系中最最基礎的行爲都沒法保證,應用程序也將一片混亂

答:這位同窗很是不錯,對jvm有必定的研究,不過咱們第一週的文章,並非說漏掉你說的這些點,而是咱們的寫做思路,是按部就班,這點很重要。

若是在剛開始就給出大段這種說明,那麼只有少數人會看懂,回到普通的那種學院派純理論的知識傳遞方法了。

你說的很好,不過但願耐心跟着繼續看,咱們會有意把不少細節放在後面講,按部就班,保證不少小白同窗都輕鬆學習,這點很重要。

問題

tomcat須要破壞雙親委派模型的緣由:

  1. tomcat中的須要支持不一樣web應用依賴同一個第三方類庫的不一樣版本,jar類庫須要保證相互隔離;

  2. 同一個第三方類庫的相同版本在不一樣web應用能夠共享

  3. tomcat自身依賴的類庫須要與應用依賴的類庫隔離 (3)jsp須要支持修改後不用重啓tomcat便可生效 爲了上面類加載隔離和類更新不用重啓,定製開發各類的類加載器

答:回答的很是好

問題

老師您好,我想問一下,咱們的應用若是關掉,建立在堆中的對象,還有方法區的數據都還在嗎?

答:應用關了,那麼系統對應的JVM進程就沒了,那JVM內存區域的數據就全沒了

問題

請問老師:

  1. 「實例對象都已經從Java堆內存裏被回收」和「Class對象沒有任何引用」是一個概念麼?

  2. 「ClassLoader已經被回收」,何時會回收?

答:

  1. 不是,Class對象表明類,若是你有變量引用了類的Class對象,那麼就是有引用

  2. 好比你自定義的ClassLoader,自己就是個對象,一旦他沒人再使用了,就會被垃圾回收

問題:引用Class對象的是該類的實例對象?仍是其餘什麼?

答:好比用反射,能夠獲取一個對象的類的Class對象實例,好比Class clazz = replicaManager.getClass(),就能夠經過replicaManager引用的對象,獲取到ReplicaManager類的Class對象,那個clazz變量,就能夠引用這個Class對象

問題

第二週打卡,跟上節奏。回答今日思考題:項目中託管給Spring管理的對象,帶@Configration的配置對象,都是長期存在老年代。

本身定義那些pojo對象,若是不被定義爲類對象就是朝生夕滅的,因此分配在年輕代裏面。

答:很是好

問題

public void load(){ A a = new A(); a這個保存地址的變量是存在虛擬機棧的,這個方法執行完成後就銷燬了,

那new A()這個對象是須要等待垃圾回收線程掃描後纔回收嗎?

仍是和a這個變量同時回收?

答:對象要等待垃圾回收才銷燬

問題

gc回收的是軟引用,弱應用和虛引用,關於軟引用和弱引用傻傻分不清,這二者有何異同,請指教


答:內存不夠纔會回收軟引用對象,內存空間足夠的話,不會回收軟引用對象。弱引用無論內存空間夠不夠,只能撐到下次垃圾回收以前,就被會回收。

問題

思考題回答:目前的系統,大部分是spring容器的對象,spring默認單實例方式加載,這些對象可能會存在老年代中。

可是方法內部new出來的對象不會存活太長時間,方法結束,引用消息,對象也會在下一次gc被回收。

答:很是好

問題

類初始化時,變量引用的是new出來的對象,此時變量引用的對象會被實例化到堆內存嗎?

答:會實例化放到堆內存

問題

老師好。我今天使用Java VisualVm看了一下,發現了一個問題,我配置的是-Xms4M -Xmx4M -Xmn2M。應該是年輕代2M 老年代2M。

寫了一個while循環不斷的在方法裏建立臨時變量對象,可是我發現當內存堆達到3m左右的時候,就發生了Minor GC,堆內存回到了2M,而不是4M的時候

理論上不該該是堆內存滿了再Minor GC嗎?

答:這個很正常的,由於後續第三週會講新生代的內存結構,其實不是新生代所有佔滿才minor gc,是裏面一塊主要的內存區域滿了,就minor gc

問題

打卡。作項目時候沒有關注系統壓力,主要是考慮功能怎麼現實,而後按時交付測試。之後能夠按老師今天這個思路去估算一下系統壓力了。

答:是的,若是沒合理估算內存壓力,沒合理設置jvm內存大小,那麼上線以後,可能會發現頻繁gc問題,致使系統卡頓,這是jvm優化的第一步,合理估算業務壓力,合理設置內存大小

問題

案例總結:

1. 分析系統壓力點在哪裏?

2. 壓力點的每秒請求數?

3. 每一個請求耗時?

4. 每一個請求消耗的內存?

5. 整個系統的全部請求重複1-4。

6. 算出部署多少臺機器?每一個機器多少內存?

思考題做答: 平時工做中不多這樣預估系統壓力,通常個人作法都是部署上去後分配一個堆內存,而後測試時再去監控GC的頻率作適當調整。

這樣作確實很被動,不少時候上線後發現和測試的GC頻率差太多,之後試試老師這種估算方法。

答:很是好

問題

上次發生內存溢出,咱們搞到凌晨5點多,最後咱們老大調大了堆內存解決的,說是因爲使用過多的靜態內部類,有地方引用到沒法釋放致使的,不過我如今尚未明白爲啥?

答:學完這個專欄,你也能掌握這種能力

問題

這篇文字最重要的收穫是分析處理問題的思路,分解而後一步一步分析處理。贊。


答:是的,思路很是的重要,按照這個思路來,大家本身也能作jvm內存壓力預估,系統上線前,合理設置一個內存大小

問題

是否是不該該在高峯期的時候讓系統進行垃圾回收,這樣會形成STW。老師大家線上系統會考慮在低峯期手動觸發垃圾回收麼?

答:建議不要手動觸發,依託合理的內存設置以及參數優化,讓系統自行運轉

問題

是否是應該經過老師說的估算方式,儘可能設大新生代 ,讓系統在高峯期不產生gc?

答:是的,儘可能是這樣

問題

老師,那無論三七二十一,在內存大的條件下,多分配給新生代就行了,若是不行就加內存?

答:那你就浪費機器資源,要合理評估,不須要大內存,就用小內存就能夠了

問題

一、支付系統高分期須要處理的請求是是否是應該這麼算:

100萬 / (24 * 3600) ≈ 12,根據28法則,大部分請求發生在中午12點到13點以及晚上的18點到19點

因此 80萬請求 / (2 * 3600) ≈ 111,即算出若是單臺每秒大概是100多個請求

二、還有就是在完整的支付系統內存佔用須要進行預估中,你提到「能夠把以前的計算結果擴大10倍~20倍。

也就是說,每秒除了內存裏建立的支付訂單對象還建立數十種對象」 這裏若是要計算的話以前的計算結果是 30 * 500字節 * 20倍 = 300000字節=300KB 是這麼算嗎?

答:沒錯的,這是大體估算的方法

問題

老師 您這兒的案例中提到,一個支付請求須要1s中,30個請求也是1s鍾,那是否是能夠理解爲開了30個線程同時併發處理支付請求入庫?


答:就是這個意思

問題

文章中寫的:

「可能你每秒過來的1000筆交易,再也不是1秒就能夠處理完畢了,由於壓力驟增,會致使你的系統性能降低,可能偶爾會出現每一個請求處理完畢須要幾秒鐘」

老師,這裏說的壓力驟增是磁盤讀寫壓力嗎仍是內存CPU壓力,出行每一個請求處理完畢須要幾秒這裏是寫入壓力嗎?與網絡有關嗎?謝謝

答:都有可能,主要是CPU負載太高,會致使高併發下每一個請求的處理性能直線降低,還有網絡問題也會有

問題

咱們訂單一天二百多萬,線上正常每秒產生也應該在1M以上,xmn2048,xmx8192,原本半個多小時一次minor gc

擴大一百倍,不到一分鐘一次,應該會出現案例中的問題,老年代會頻繁gc,不過咱們有6g老年代,達到full gc應該時間會稍微長點

答:本身分析的很是好,掌握這個方法了

問題

老師, 能夠說下, 爲何併發上來了, 壓力就會劇增嘛? 哪些方面的壓力.

答:併發上來以後,內存、網絡帶寬、磁盤IO、數據庫,都是系統的瓶頸,好比網絡帶寬打滿,你的請求就會排隊卡住,磁盤IO變滿,數據庫性能降低,都會致使請求處理慢幾十倍

問題

您好,我有一個問題,就是main函數中建立了對象,這個對象在堆中開啓空間,那麼若是這個對象中有成員變量,這個成員變量是存在哪裏?成員變量的引用存在哪裏

答:成員變量也是在堆內存裏的

問題

老師我上網查了一下資料,把問題弄明白了。Test.class是被加載了,可是並無 執行初始化步驟。

課程中提到了類加載的時機,可是沒有提到類初始化的時機,我把一直理解類的 加載->驗證->準備->解析->初始化是一個連續的動做,覺得類一旦加載一定 會當即初始化。

補充類初始化的時機以下:

  1. 當建立某個類的新實例時(如經過new或者反射,克隆,反序列化等)

  2. 當調用某個類的靜態方法時

  3. 當使用某個類或接口的靜態字段時

  4. 調用Java API中的某些反射方法時,好比類Class中的方法,或者java.lang.reflect中的類的方法時

  5. 當初始化某個子類時

  6. 當虛擬機啓動某個被標明爲啓動類的類(即包含main方法的那個類) 因此System.ou.println(Test.class)不知足上面6種狀況,也就沒有作初始化

答:對的,就是這樣

問題

老師 假如Kafka類裏面 聲明一個實例變量 private ReplicaFetcher = new ... 這個實例變量放在哪一個區

答:實例變量就在堆內存裏

問題

老師 根據示例代碼, 我作了如下jvm參數配置:-Xms10m -Xmx10m, 而後在visualVM裏跟蹤堆棧使用狀況。

十分詫異的現象是:在while true循環中,也就是執行fetchFromRemote的時候, 新生代大小一直在有規律的增加,而後不停的minor GC, 每次GC(而不是等到15次之後),老年代都會相應的增加一點。

個人問題是,使新生代增加的究竟是什麼對象?GC時又是什麼對象跑到老年代裏去了?

按個人理解,fetcher對象應該有且只有一份實例,並且while循環中,不會生成新的對象,

最初,新生代裏有一份fetcher,而後第16次minor GC的時候,fetcher被轉移到老年代, 不管如何,新生代和老年代都不會不斷增加。

因此,是否是有什麼我不知道的對象混了進去,致使新生代不斷增加?

答:新生代到老年代轉移的機制不僅是年齡一種,還有別的,下週會詳細說明

問題

老師,每一個訂單處理時間是1秒和10秒,10秒的就要比1秒的要多加內存嗎?請問是怎樣的邏輯?可否量化?

答:那你得計算一下,你的內存每秒被使用的速度,根據這個來規劃內存大小

還有你要是10秒一個請求,可能內存裏累計起來會有大量對象無法釋放,會致使瞬間新生代被打滿

並且大量對象無法回收,而後所有去老年代,而後老年代也很快就滿了,最後內存不夠,很快就內存溢出了

END

推薦一個專欄:

《從零開始帶你成爲JVM實戰高手》

做者是我多年好友,之前團隊的左膀右臂

一塊兒經歷過各類大型複雜系統上線的血雨腥風

現任阿里資深技術專家,對JVM有豐富的生產實踐經驗

專欄目錄參見文末,能夠掃下方海報進行試讀

經過上面海報購買,再返你24元

領取方式:加微信號:Giotto1245,暗號:返現

相關文章
相關標籤/搜索