本文來(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需要破壞雙親委派模型的原因:
tomcat中的需要支持不同web應(yīng)用依賴同一個(gè)第三方類庫(kù)的不同版本,jar類庫(kù)需要保證相互隔離;
同一個(gè)第三方類庫(kù)的相同版本在不同web應(yīng)用可以共享
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)老師:
“實(shí)例對(duì)象都已經(jīng)從Java堆內(nèi)存里被回收”和“Class對(duì)象沒(méi)有任何引用”是一個(gè)概念么?
“ClassLoader已經(jīng)被回收”,什么時(shí)候會(huì)回收?
答:
不是,Class對(duì)象代表類,如果你有變量引用了類的Class對(duì)象,那么就是有引用
比如你自定義的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ī)如下:
當(dāng)創(chuàng)建某個(gè)類的新實(shí)例時(shí)(如通過(guò)new或者反射,克隆,反序列化等)
當(dāng)調(diào)用某個(gè)類的靜態(tài)方法時(shí)
當(dāng)使用某個(gè)類或接口的靜態(tài)字段時(shí)
調(diào)用Java API中的某些反射方法時(shí),比如類Class中的方法,或者java.lang.reflect中的類的方法時(shí)
當(dāng)初始化某個(gè)子類時(shí)
當(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)存溢出了