【進(jìn)階JVM】32個(gè)Java虛擬機(jī)知識(shí)點(diǎn)

本文來(lái)源:《從零開(kāi)始帶你成為JVM實(shí)戰(zhàn)高手》

第二周答疑匯總

問(wèn)題

既然棧幀存放了方法對(duì)應(yīng)的局部變量的數(shù)據(jù),也包括了方法執(zhí)行的其它相關(guān)信息。

那為什么不把程序計(jì)算器那塊記錄執(zhí)行的情況,也放在各個(gè)方法自己的棧幀里,而是要單獨(dú)列一個(gè)程序計(jì)數(shù)區(qū)去存儲(chǔ)呢?

答:這就是JVM設(shè)計(jì)者的設(shè)計(jì)思想了,因?yàn)槌绦蛴?jì)數(shù)器針對(duì)的是代碼指令的執(zhí)行,Java虛擬棧針對(duì)的是放方法的數(shù)據(jù),一個(gè)是指令,一個(gè)是數(shù)據(jù),分開(kāi)設(shè)計(jì)

問(wèn)題

思考題回答:什么情況下一個(gè)類會(huì)被回收?

  • 首先該類的所有實(shí)例(堆中)都已經(jīng)被回收;

  • 其次該類的ClassLoader已經(jīng)被回收;

  • 最后,對(duì)該類對(duì)應(yīng)的Class對(duì)象沒(méi)有任何引用。滿足上面三個(gè)條件就可以回收該類了。

答:正解

問(wèn)題

方法執(zhí)行完后, 棧幀立馬被出棧, 那該棧幀中的變量等數(shù)據(jù)是立馬就被回收掉嗎?還是需要等垃圾回收線程掃描到再回收掉?

答:出棧就沒(méi)了

問(wèn)題

如果把public static int flushInterval = Configuration.getInt("xxx");中的static去掉, 那后面的getInt是在什么時(shí)候執(zhí)行的呢 ?

我自己測(cè)試了一下,好像是在構(gòu)造方法之前執(zhí)行的, 不明白這個(gè)到底屬于什么階段?

答:這是屬于類的對(duì)象實(shí)例初始化的階段

問(wèn)題

雙親委派模型設(shè)計(jì)的出發(fā)點(diǎn)很重要,文章漏了。對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在Java虛擬機(jī)中的唯一性,每一個(gè)類加載器,都擁有一個(gè)獨(dú)立的類名稱空間。

也就是說(shuō),判斷2個(gè)類是否“相等”,只有在這2個(gè)類是由同一個(gè)類加載器加載的前提下才有意義

否則即使這2個(gè)類來(lái)源于同一個(gè)Class文件,被同一個(gè)虛擬機(jī)加載,只要加載它們的類加載器不同,這2個(gè)類必定不相等。

基于雙親委派模型設(shè)計(jì),那么Java中基礎(chǔ)的類,Object類似Object類重復(fù)多次的問(wèn)題就不會(huì)存在了

因?yàn)榻?jīng)過(guò)層層傳遞,加載請(qǐng)求最終都會(huì)被Bootstrap ClassLoader所響應(yīng)。加載的Object類也會(huì)只有一個(gè)

否則如果用戶自己編寫了一個(gè)java.lang.Object類,并把它放到了ClassPath中,會(huì)出現(xiàn)很多個(gè)Object類,這樣Java類型體系中最最基礎(chǔ)的行為都無(wú)法保證,應(yīng)用程序也將一片混亂

答:這位同學(xué)非常不錯(cuò),對(duì)jvm有一定的研究,不過(guò)我們第一周的文章,并不是說(shuō)漏掉你說(shuō)的這些點(diǎn),而是我們的寫作思路,是循序漸進(jìn),這點(diǎn)很重要。

如果在剛開(kāi)始就給出大段這種說(shuō)明,那么只有少數(shù)人會(huì)看懂,回到普通的那種學(xué)院派純理論的知識(shí)傳遞方法了。

你說(shuō)的很好,不過(guò)希望耐心跟著繼續(xù)看,我們會(huì)有意把很多細(xì)節(jié)放在后面講,循序漸進(jìn),保證很多小白同學(xué)都輕松學(xué)習(xí),這點(diǎn)很重要。

問(wèn)題

tomcat需要破壞雙親委派模型的原因:

  1. tomcat中的需要支持不同web應(yīng)用依賴同一個(gè)第三方類庫(kù)的不同版本,jar類庫(kù)需要保證相互隔離;

  2. 同一個(gè)第三方類庫(kù)的相同版本在不同web應(yīng)用可以共享

  3. tomcat自身依賴的類庫(kù)需要與應(yīng)用依賴的類庫(kù)隔離 (3)jsp需要支持修改后不用重啟tomcat即可生效 為了上面類加載隔離和類更新不用重啟,定制開(kāi)發(fā)各種的類加載器

答:回答的非常好

問(wèn)題

老師您好,我想問(wèn)一下,我們的應(yīng)用如果關(guān)掉,創(chuàng)建在堆中的對(duì)象,還有方法區(qū)的數(shù)據(jù)都還在嗎?

答:應(yīng)用關(guān)了,那么系統(tǒng)對(duì)應(yīng)的JVM進(jìn)程就沒(méi)了,那JVM內(nèi)存區(qū)域的數(shù)據(jù)就全沒(méi)了

問(wèn)題

請(qǐng)問(wèn)老師:

  1. “實(shí)例對(duì)象都已經(jīng)從Java堆內(nèi)存里被回收”和“Class對(duì)象沒(méi)有任何引用”是一個(gè)概念么?

  2. “ClassLoader已經(jīng)被回收”,什么時(shí)候會(huì)回收?

答:

  1. 不是,Class對(duì)象代表類,如果你有變量引用了類的Class對(duì)象,那么就是有引用

  2. 比如你自定義的ClassLoader,本身就是個(gè)對(duì)象,一旦他沒(méi)人再使用了,就會(huì)被垃圾回收

問(wèn)題:引用Class對(duì)象的是該類的實(shí)例對(duì)象?還是其他什么?

答:比如用反射,可以獲取一個(gè)對(duì)象的類的Class對(duì)象實(shí)例,比如Class clazz = replicaManager.getClass(),就可以通過(guò)replicaManager引用的對(duì)象,獲取到ReplicaManager類的Class對(duì)象,那個(gè)clazz變量,就可以引用這個(gè)Class對(duì)象

問(wèn)題

第二周打卡,跟上節(jié)奏?;卮鸾袢账伎碱}:項(xiàng)目中托管給Spring管理的對(duì)象,帶@Configration的配置對(duì)象,都是長(zhǎng)期存在老年代。

自己定義那些pojo對(duì)象,如果不被定義為類對(duì)象就是朝生夕滅的,所以分配在年輕代里面。

答:非常好

問(wèn)題

public void load(){ A a = new A(); a這個(gè)保存地址的變量是存在虛擬機(jī)棧的,這個(gè)方法執(zhí)行完成后就銷毀了,

那new A()這個(gè)對(duì)象是需要等待垃圾回收線程掃描后才回收嗎?

還是和a這個(gè)變量同時(shí)回收?

答:對(duì)象要等待垃圾回收才銷毀

問(wèn)題

gc回收的是軟引用,弱應(yīng)用和虛引用,關(guān)于軟引用和弱引用傻傻分不清,這兩者有何異同,請(qǐng)指教

答:內(nèi)存不夠才會(huì)回收軟引用對(duì)象,內(nèi)存空間足夠的話,不會(huì)回收軟引用對(duì)象。弱引用不管內(nèi)存空間夠不夠,只能撐到下次垃圾回收之前,就被會(huì)回收。

問(wèn)題

思考題回答:目前的系統(tǒng),大部分是spring容器的對(duì)象,spring默認(rèn)單實(shí)例方式加載,這些對(duì)象可能會(huì)存在老年代中。

但是方法內(nèi)部new出來(lái)的對(duì)象不會(huì)存活太長(zhǎng)時(shí)間,方法結(jié)束,引用消息,對(duì)象也會(huì)在下一次gc被回收。

答:非常好

問(wèn)題

類初始化時(shí),變量引用的是new出來(lái)的對(duì)象,此時(shí)變量引用的對(duì)象會(huì)被實(shí)例化到堆內(nèi)存嗎?

答:會(huì)實(shí)例化放到堆內(nèi)存

問(wèn)題

老師好。我今天使用Java VisualVm看了一下,發(fā)現(xiàn)了一個(gè)問(wèn)題,我配置的是-Xms4M -Xmx4M -Xmn2M。應(yīng)該是年輕代2M 老年代2M。

寫了一個(gè)while循環(huán)不斷的在方法里創(chuàng)建臨時(shí)變量對(duì)象,但是我發(fā)現(xiàn)當(dāng)內(nèi)存堆達(dá)到3m左右的時(shí)候,就發(fā)生了Minor GC,堆內(nèi)存回到了2M,而不是4M的時(shí)候

理論上不應(yīng)該是堆內(nèi)存滿了再M(fèi)inor GC嗎?

答:這個(gè)很正常的,因?yàn)楹罄m(xù)第三周會(huì)講新生代的內(nèi)存結(jié)構(gòu),其實(shí)不是新生代全部占滿才minor gc,是里面一塊主要的內(nèi)存區(qū)域滿了,就minor gc

問(wèn)題

打卡。做項(xiàng)目時(shí)候沒(méi)有關(guān)注系統(tǒng)壓力,主要是考慮功能怎么現(xiàn)實(shí),然后按時(shí)交付測(cè)試。以后可以按老師今天這個(gè)思路去估算一下系統(tǒng)壓力了。

答:是的,如果沒(méi)合理估算內(nèi)存壓力,沒(méi)合理設(shè)置jvm內(nèi)存大小,那么上線之后,可能會(huì)發(fā)現(xiàn)頻繁gc問(wèn)題,導(dǎo)致系統(tǒng)卡頓,這是jvm優(yōu)化的第一步,合理估算業(yè)務(wù)壓力,合理設(shè)置內(nèi)存大小

問(wèn)題

案例總結(jié):

1. 分析系統(tǒng)壓力點(diǎn)在哪里?

2. 壓力點(diǎn)的每秒請(qǐng)求數(shù)?

3. 每個(gè)請(qǐng)求耗時(shí)?

4. 每個(gè)請(qǐng)求消耗的內(nèi)存?

5. 整個(gè)系統(tǒng)的所有請(qǐng)求重復(fù)1-4。

6. 算出部署多少臺(tái)機(jī)器?每個(gè)機(jī)器多少內(nèi)存?

思考題作答: 平時(shí)工作中很少這樣預(yù)估系統(tǒng)壓力,一般我的做法都是部署上去后分配一個(gè)堆內(nèi)存,然后測(cè)試時(shí)再去監(jiān)控GC的頻率做適當(dāng)調(diào)整。

這樣做確實(shí)很被動(dòng),很多時(shí)候上線后發(fā)現(xiàn)和測(cè)試的GC頻率差太多,以后試試?yán)蠋熯@種估算方法。

答:非常好

問(wèn)題

上次發(fā)生內(nèi)存溢出,我們搞到凌晨5點(diǎn)多,最后我們老大調(diào)大了堆內(nèi)存解決的,說(shuō)是由于使用過(guò)多的靜態(tài)內(nèi)部類,有地方引用到無(wú)法釋放導(dǎo)致的,不過(guò)我現(xiàn)在還沒(méi)有明白為啥?

答:學(xué)完這個(gè)專欄,你也能掌握這種能力

問(wèn)題

這篇文字最重要的收獲是分析處理問(wèn)題的思路,分解然后一步一步分析處理。贊。

答:是的,思路非常的重要,按照這個(gè)思路來(lái),你們自己也能做jvm內(nèi)存壓力預(yù)估,系統(tǒng)上線前,合理設(shè)置一個(gè)內(nèi)存大小

問(wèn)題

是不是不應(yīng)該在高峰期的時(shí)候讓系統(tǒng)進(jìn)行垃圾回收,這樣會(huì)造成STW。老師你們線上系統(tǒng)會(huì)考慮在低峰期手動(dòng)觸發(fā)垃圾回收么?

答:建議不要手動(dòng)觸發(fā),依托合理的內(nèi)存設(shè)置以及參數(shù)優(yōu)化,讓系統(tǒng)自行運(yùn)轉(zhuǎn)

問(wèn)題

是不是應(yīng)該通過(guò)老師說(shuō)的估算方式,盡量設(shè)大新生代 ,讓系統(tǒng)在高峰期不產(chǎn)生gc?

答:是的,盡量是這樣

問(wèn)題

老師,那不管三七二十一,在內(nèi)存大的條件下,多分配給新生代就好了,如果不行就加內(nèi)存?

答:那你就浪費(fèi)機(jī)器資源,要合理評(píng)估,不需要大內(nèi)存,就用小內(nèi)存就可以了

問(wèn)題

1、支付系統(tǒng)高分期需要處理的請(qǐng)求是是不是應(yīng)該這么算:

100萬(wàn) / (24 * 3600) ≈ 12,根據(jù)28法則,大部分請(qǐng)求發(fā)生在中午12點(diǎn)到13點(diǎn)以及晚上的18點(diǎn)到19點(diǎn)

所以 80萬(wàn)請(qǐng)求 / (2 * 3600) ≈ 111,即算出如果單臺(tái)每秒大概是100多個(gè)請(qǐng)求

2、還有就是在完整的支付系統(tǒng)內(nèi)存占用需要進(jìn)行預(yù)估中,你提到“可以把之前的計(jì)算結(jié)果擴(kuò)大10倍~20倍。

也就是說(shuō),每秒除了內(nèi)存里創(chuàng)建的支付訂單對(duì)象還創(chuàng)建數(shù)十種對(duì)象” 這里如果要計(jì)算的話之前的計(jì)算結(jié)果是 30 * 500字節(jié) * 20倍 = 300000字節(jié)=300KB 是這么算嗎?

答:沒(méi)錯(cuò)的,這是大致估算的方法

問(wèn)題

老師 您這兒的案例中提到,一個(gè)支付請(qǐng)求需要1s中,30個(gè)請(qǐng)求也是1s鐘,那是不是可以理解為開(kāi)了30個(gè)線程同時(shí)并發(fā)處理支付請(qǐng)求入庫(kù)?

答:就是這個(gè)意思

問(wèn)題

文章中寫的:

“可能你每秒過(guò)來(lái)的1000筆交易,不再是1秒就可以處理完畢了,因?yàn)閴毫E增,會(huì)導(dǎo)致你的系統(tǒng)性能下降,可能偶爾會(huì)出現(xiàn)每個(gè)請(qǐng)求處理完畢需要幾秒鐘”

老師,這里說(shuō)的壓力驟增是磁盤讀寫壓力嗎還是內(nèi)存CPU壓力,出行每個(gè)請(qǐng)求處理完畢需要幾秒這里是寫入壓力嗎?與網(wǎng)絡(luò)有關(guān)嗎?謝謝

答:都有可能,主要是CPU負(fù)載過(guò)高,會(huì)導(dǎo)致高并發(fā)下每個(gè)請(qǐng)求的處理性能直線下降,還有網(wǎng)絡(luò)問(wèn)題也會(huì)有

問(wèn)題

我們訂單一天二百多萬(wàn),線上正常每秒產(chǎn)生也應(yīng)該在1M以上,xmn2048,xmx8192,本來(lái)半個(gè)多小時(shí)一次minor gc

擴(kuò)大一百倍,不到一分鐘一次,應(yīng)該會(huì)出現(xiàn)案例中的問(wèn)題,老年代會(huì)頻繁gc,不過(guò)我們有6g老年代,達(dá)到full gc應(yīng)該時(shí)間會(huì)稍微長(zhǎng)點(diǎn)

答:自己分析的非常好,掌握這個(gè)方法了

問(wèn)題

老師, 可以說(shuō)下, 為什么并發(fā)上來(lái)了, 壓力就會(huì)劇增嘛? 哪些方面的壓力.

答:并發(fā)上來(lái)之后,內(nèi)存、網(wǎng)絡(luò)帶寬、磁盤IO、數(shù)據(jù)庫(kù),都是系統(tǒng)的瓶頸,比如網(wǎng)絡(luò)帶寬打滿,你的請(qǐng)求就會(huì)排隊(duì)卡住,磁盤IO變滿,數(shù)據(jù)庫(kù)性能下降,都會(huì)導(dǎo)致請(qǐng)求處理慢幾十倍

問(wèn)題

您好,我有一個(gè)問(wèn)題,就是main函數(shù)中創(chuàng)建了對(duì)象,這個(gè)對(duì)象在堆中開(kāi)啟空間,那么如果這個(gè)對(duì)象中有成員變量,這個(gè)成員變量是存在哪里?成員變量的引用存在哪里

答:成員變量也是在堆內(nèi)存里的

問(wèn)題

老師我上網(wǎng)查了一下資料,把問(wèn)題弄明白了。Test.class是被加載了,但是并沒(méi)有 執(zhí)行初始化步驟。

課程中提到了類加載的時(shí)機(jī),但是沒(méi)有提到類初始化的時(shí)機(jī),我把一直理解類的 加載->驗(yàn)證->準(zhǔn)備->解析->初始化是一個(gè)連續(xù)的動(dòng)作,以為類一旦加載必定 會(huì)立即初始化。

補(bǔ)充類初始化的時(shí)機(jī)如下:

  1. 當(dāng)創(chuàng)建某個(gè)類的新實(shí)例時(shí)(如通過(guò)new或者反射,克隆,反序列化等)

  2. 當(dāng)調(diào)用某個(gè)類的靜態(tài)方法時(shí)

  3. 當(dāng)使用某個(gè)類或接口的靜態(tài)字段時(shí)

  4. 調(diào)用Java API中的某些反射方法時(shí),比如類Class中的方法,或者java.lang.reflect中的類的方法時(shí)

  5. 當(dāng)初始化某個(gè)子類時(shí)

  6. 當(dāng)虛擬機(jī)啟動(dòng)某個(gè)被標(biāo)明為啟動(dòng)類的類(即包含main方法的那個(gè)類) 所以System.ou.println(Test.class)不滿足上面6種情況,也就沒(méi)有做初始化

答:對(duì)的,就是這樣

問(wèn)題

老師 假如Kafka類里面 聲明一個(gè)實(shí)例變量 private ReplicaFetcher = new ... 這個(gè)實(shí)例變量放在哪個(gè)區(qū)

答:實(shí)例變量就在堆內(nèi)存里

問(wèn)題

老師 根據(jù)示例代碼, 我做了以下jvm參數(shù)配置:-Xms10m -Xmx10m, 然后在visualVM里跟蹤堆棧使用情況。

十分詫異的現(xiàn)象是:在while true循環(huán)中,也就是執(zhí)行fetchFromRemote的時(shí)候, 新生代大小一直在有規(guī)律的增長(zhǎng),然后不停的minor GC, 每次GC(而不是等到15次以后),老年代都會(huì)相應(yīng)的增長(zhǎng)一點(diǎn)。

我的問(wèn)題是,使新生代增長(zhǎng)的到底是什么對(duì)象?GC時(shí)又是什么對(duì)象跑到老年代里去了?

按我的理解,fetcher對(duì)象應(yīng)該有且只有一份實(shí)例,而且while循環(huán)中,不會(huì)生成新的對(duì)象,

最初,新生代里有一份fetcher,然后第16次minor GC的時(shí)候,fetcher被轉(zhuǎn)移到老年代, 無(wú)論如何,新生代和老年代都不會(huì)不斷增長(zhǎng)。

所以,是不是有什么我不知道的對(duì)象混了進(jìn)去,導(dǎo)致新生代不斷增長(zhǎng)?

答:新生代到老年代轉(zhuǎn)移的機(jī)制不只是年齡一種,還有別的,下周會(huì)詳細(xì)說(shuō)明

問(wèn)題

老師,每個(gè)訂單處理時(shí)間是1秒和10秒,10秒的就要比1秒的要多加內(nèi)存嗎?請(qǐng)問(wèn)是怎樣的邏輯?能否量化?

答:那你得計(jì)算一下,你的內(nèi)存每秒被使用的速度,根據(jù)這個(gè)來(lái)規(guī)劃內(nèi)存大小

還有你要是10秒一個(gè)請(qǐng)求,可能內(nèi)存里累計(jì)起來(lái)會(huì)有大量對(duì)象沒(méi)法釋放,會(huì)導(dǎo)致瞬間新生代被打滿

而且大量對(duì)象沒(méi)法回收,然后全部去老年代,然后老年代也很快就滿了,最后內(nèi)存不夠,很快就內(nèi)存溢出了

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一 、java虛擬機(jī)底層結(jié)構(gòu)詳解 我們知道,一個(gè)JVM實(shí)例的行為不光是它自己的事,還涉及到它的子系統(tǒng)、存儲(chǔ)區(qū)域、...
    葡萄喃喃囈語(yǔ)閱讀 1,582評(píng)論 0 4
  • 第二部分 自動(dòng)內(nèi)存管理機(jī)制 第二章 java內(nèi)存異常與內(nèi)存溢出異常 運(yùn)行數(shù)據(jù)區(qū)域 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)...
    小明oh閱讀 1,292評(píng)論 0 2
  • 《深入理解Java虛擬機(jī)》筆記_第一遍 先取看完這本書(JVM)后必須掌握的部分。 第一部分 走近 Java 從傳...
    xiaogmail閱讀 5,476評(píng)論 1 34
  • 一、運(yùn)行時(shí)數(shù)據(jù)區(qū)域 Java虛擬機(jī)管理的內(nèi)存包括幾個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧、本地方法棧、堆、程序計(jì)數(shù)器,...
    luhanlin閱讀 611評(píng)論 0 0
  • 1. Java 內(nèi)存區(qū)域與內(nèi)存溢出異常 1.1 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 根據(jù)《Java 虛擬機(jī)規(guī)范(Java SE 7 版...
    java大濕兄閱讀 746評(píng)論 0 20

友情鏈接更多精彩內(nèi)容