溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JVM+Redis+SpringBoot的面試題有哪些

發(fā)布時(shí)間:2022-03-29 14:14:26 來源:億速云 閱讀:151 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要介紹“JVM+Redis+SpringBoot的面試題有哪些”,在日常操作中,相信很多人在JVM+Redis+SpringBoot的面試題有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”JVM+Redis+SpringBoot的面試題有哪些”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

1、JVM

1.1、類加載機(jī)制

加載:把.java文件編譯成.class文件,生成Class對象
驗(yàn)證:驗(yàn)證字節(jié)碼的準(zhǔn)確性
準(zhǔn)備:給類的靜態(tài)變量做分配內(nèi)存,并賦予默認(rèn)值
解析:符號引用和動態(tài)鏈接都變?yōu)橹苯右?
初始化:給類的靜態(tài)變量初始化為指定的值,執(zhí)行靜態(tài)代碼塊

1.2、類加載器

1、根類加載器(Bootstrap classLoader):負(fù)責(zé)加載lib下的核心類庫2、擴(kuò)展加載器(ExtClassLoader):負(fù)責(zé)加載lib目錄下的ext的jar類包3、應(yīng)用加載器(AppClassLoader):負(fù)責(zé)加載ClassPath路勁下的類包(自定義的類)4、自定義類加載器:繼承ClassLoader,重寫loadClass(),findClass(),一般是只需要重寫findClass

1.3、雙親委派機(jī)制

雙親加載機(jī)制中源碼有兩個方法:1、loadClass	1)先檢查指定的類是否已經(jīng)加載過了,若已經(jīng)加載過,則直接返回加載的類	2)若沒有加載,則判斷有沒有父類,有的話則調(diào)用父類加載器,或者調(diào)用根類加載器(Bootstrap)加載。	3)若父類加載器與Bootstrap加載器都沒有找到指定的類,則調(diào)用下面的方法(findClass)來完成類加載2、findClass

1.4、為什么要設(shè)計(jì)雙親加載機(jī)制

1、保證類的唯一性2、沙箱安全機(jī)制

1.5、全盤委托機(jī)制

如果沒有顯示的使用其他類加載器,則類下的所有依賴與及引用的類都將會有加載該類的類加載器加載

1.6、Tomcat如何自定義類加載機(jī)制

1、CommonLoader:Tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個Webapp訪問;2、CatalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對于Webapp不可見;3、SharedLoader:各個Webapp共享的類加載器,加載路徑中的class對于所有Webapp可見,但是對于Tomcat容器不可見;4、WebappClassLoader:各個Webapp私有的類加載器,加載路徑中的class只對當(dāng)前Webapp可見,比如加載war包里相關(guān)的類, 每個war包應(yīng)用都有自己的WebappClassLoader,實(shí)現(xiàn)相互隔離,比如不同war包應(yīng)用引入了不同的spring版本,這樣實(shí)現(xiàn)就能加載各自的spring版本;5、模擬實(shí)現(xiàn)Tomcat的JasperLoader熱加載
  	原理:后臺啟動線程監(jiān)聽jsp文件變化,如果變化了找到該jsp對應(yīng)的servlet類的加載器引用(gcroot),重新生成新的JasperLoader加載器賦值給引用,然后加載新的jsp對應(yīng)的servlet類,之前的那個加載器因?yàn)闆]有g(shù)croot引用了,下一次gc的時(shí)候會被銷毀    
=>總結(jié):每個webappClassLoader加載自己的目錄下的class文件,不會傳遞給父類加載器,打破了雙親委派機(jī)制。

1.7、內(nèi)存模型

私有:
	程序計(jì)時(shí)器:記錄當(dāng)前線程執(zhí)行到字節(jié)碼行號
	虛擬機(jī)棧:內(nèi)部有許多棧幀,每個棧幀里面包括局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口。
 	本地方法棧:執(zhí)行本地的Native方法
共享:
 	堆:內(nèi)部分為eden區(qū),s0,s1,老年代,保存對象和數(shù)組
    方法區(qū)/永久代(1.8后元空間):保存類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼;內(nèi)部有個運(yùn)行時(shí)常量池,用于保存類的版本、字段、方法、接口等;

擴(kuò)展=>直接內(nèi)存:通過unsafe,或者netty的DirectByteBuffer申請

1.8、對象創(chuàng)建與分配

1.8.1、創(chuàng)建

1、類加載檢查

	虛擬機(jī)遇到一條new指令時(shí),首先將去檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,那必須先執(zhí)行相應(yīng)的類加載過程。	new指令對應(yīng)到語言層面上講是,new關(guān)鍵詞、對象克隆、對象序列化等

2、分配內(nèi)存

//劃分內(nèi)存	1、指針碰撞
內(nèi)存規(guī)整,用過的內(nèi)存放一邊,沒用過的放一邊2、空閑列表
內(nèi)存不規(guī)整,使用的和空閑的相互交錯,需要一個列表進(jìn)行存儲//并發(fā)問題解決1、CAS2、本地線程分配緩沖區(qū)(TLAB)把內(nèi)存分配的動作按照線程劃分在不同的空間之中進(jìn)行,即每個線程在Java堆中預(yù)先分配一小塊內(nèi)存。通過XX:+/-UseTLAB參數(shù)來設(shè)定虛擬機(jī)是否使用TLAB(JVM會默認(rèn)開啟XX:+UseTLAB),XX:TLABSize指定TLAB大小。

3、初始化

	為分配到的內(nèi)存初始化為零值,不設(shè)置對象頭,若是呀TLAB,可以提前至TLAB分配時(shí)進(jìn)行,保證對象即使不賦初始值也可以直接使用

4、設(shè)置對象頭

對象布局:1、對象頭(Header)2、實(shí)例數(shù)據(jù)(Instance Data)3、對齊填充(Padding)

JVM+Redis+SpringBoot的面試題有哪些

5、執(zhí)行方法

執(zhí)行<init>方法&#xff0c;也就是所謂的屬性賦值與執(zhí)行構(gòu)造器

1.8.2、分配

1、棧上分配
    通過逃逸分析確定該對象不會被外部訪問。如果不會逃逸可以將該對象在棧上分配內(nèi)存&#xff0c;這樣該對象所占用的內(nèi)存空間就可以隨棧幀出棧而銷毀&#xff0c;就減輕了垃圾回收的壓力。/**
    *對象逃逸分析&#xff1a;分析對象動態(tài)作用域&#xff0c;當(dāng)一個對象在方法中被定義后&#xff0c;它可能被外部方法所引用&#xff0c;例如作為調(diào)用參數(shù)傳遞到其他地方中
    *標(biāo)量替換&#xff1a;通過逃逸分析確定該對象不會被外部訪問&#xff0c;并且對象可以被進(jìn)一步分解時(shí)&#xff0c;JVM不會創(chuàng)建該對象&#xff0c;而是將該對象成員變量分解若干個被這個方法使用的成員變量所	   代替&#xff0c;這些代替的成員變量在棧幀或寄存器上分配空間&#xff0c;這樣就不會因?yàn)闆]有一大塊連續(xù)空間導(dǎo)致對象內(nèi)存不夠分配
    */'結(jié)論&#xff1a;棧上分配依賴于逃逸分析和標(biāo)量替換'2、堆上分配&#xff08;eden區(qū)&#xff09;	1、先eden區(qū)分配&#xff0c;滿了young GC&#xff0c;把存活的對象放入s02、再eden區(qū)分配&#xff0c;滿了young GC&#xff0c;把s0存活的對象和eden區(qū)存活的對象放入s1&#xff0c;3、重復(fù)1&#xff0c;2操作3、大對象進(jìn)入老年代
	大量連續(xù)的內(nèi)存空間的對象4、長期存活對象進(jìn)入老年代
	在2&#xff08;堆上分配&#xff09;中&#xff0c;每次移動都會給當(dāng)前對象設(shè)置個計(jì)數(shù)器&#xff0c;默認(rèn)15&#xff0c;CMS默認(rèn)6&#xff0c;則會young gc放入老年代5、對象動態(tài)年齡判斷
	當(dāng)一批對象的總大小大于s區(qū)內(nèi)存大小的50%&#xff0c;則大于等于這批對象年齡最大值的對象&#xff0c;就可以進(jìn)入老年代6、空間擔(dān)保機(jī)制
    年輕代每次young gc之前JVM都會計(jì)算下老年代剩余可用空間&#xff0c;如果這個可用空間小于年輕代里現(xiàn)有的所有對象大小之和(包括垃圾對象)&#xff0c;就會看一個“-XX:-HandlePromotionFailure”(jdk1.8默認(rèn)就設(shè)置了) 的參數(shù)是否設(shè)置了&#xff0c;如果有這個參數(shù)&#xff0c;就會看看老年代的可用內(nèi)存大小&#xff0c;是否大于之前每一次minor gc后進(jìn)入老年代的對象的平均大小。小于或者之前說的參數(shù)沒有設(shè)置&#xff0c;那么就會觸發(fā)一次Full gc&#xff0c;對老年代和年輕代一起回收一次垃圾&#xff0c;如果回收完還是沒有足夠空間存放新的對象就會發(fā)生"OOM"。

1.9、何判斷一個類是無用的類

1、該類所有的實(shí)例都已經(jīng)被回收&#xff0c;也就是 Java 堆中不存在該類的任何實(shí)例。2、加載該類的 ClassLoader 已經(jīng)被回收。3、該類對應(yīng)的 java.lang.Class 對象沒有在任何地方被引用&#xff0c;無法在任何地方通過反射訪問該類的方法。

1.10、finalize()方法最終判定對象是否存活

1. 第一次標(biāo)記并進(jìn)行一次篩選。
    篩選的條件是此對象是否有必要執(zhí)行finalize()方法。
    當(dāng)對象沒有覆蓋finalize方法&#xff0c;對象將直接被回收。2. 第二次標(biāo)記
    如果這個對象覆蓋了finalize方法&#xff0c;finalize方法是對象脫逃死亡命運(yùn)的最后一次機(jī)會&#xff0c;如果對象要在finalize()中成功拯救 自己&#xff0c;只要重新與引用鏈上的任何的一個對象建立關(guān)聯(lián)即可&#xff0c;譬如把自己賦值給某個類變量或?qū)ο蟮某蓡T變量&#xff0c;那在第 二次標(biāo)記時(shí)它將移除出“即將回收”的集合。如果對象這時(shí)候還沒逃脫&#xff0c;那基本上它就真的被回收了。//注意&#xff1a;一個對象的finalize()方法只會被執(zhí)行一次&#xff0c;也就是說通過調(diào)用finalize方法自我救命的機(jī)會就一次。

1.11、四大引用

1、強(qiáng)引用&#xff1a;普通的變量引用2、軟引用&#xff08;SoftReference&#xff09;&#xff1a;將對象用SoftReference軟引用類型的對象包裹&#xff0c;正常情況不會被回收&#xff0c;但是GC做完后發(fā)現(xiàn)釋放不出空間存放新的對象&#xff0c;則會把這些軟引用的對象回收掉。軟引用可用來實(shí)現(xiàn)內(nèi)存敏感的高速緩存。	//使用場景&#xff1a;瀏覽器的后退按鈕3、弱引用&#xff08;WeakReference&#xff09;&#xff1a;將對象用WeakReference軟引用類型的對象包裹&#xff0c;弱引用跟沒引用差不多&#xff0c;GC會直接回收掉&#xff0c;很少用4、虛引用&#xff1a;虛引用也稱為幽靈引用或者幻影引用&#xff0c;它是最弱的一種引用關(guān)系&#xff0c;幾乎不用

1.12、對象回收算法

1、引用計(jì)數(shù)法&#xff1a;循環(huán)引用無法解決2、Gc root算法
    將“GC Roots” 對象作為起點(diǎn)&#xff0c;從這些節(jié)點(diǎn)開始向下搜索引用的對象&#xff0c;找到的對象都標(biāo)記為非垃圾對象&#xff0c;其余未標(biāo)記的對象都是垃圾對象
GC Roots根節(jié)點(diǎn)&#xff1a;線程棧的本地變量、靜態(tài)變量、本地方法棧的變量等等

1.13、四大垃圾回收算法

1、標(biāo)記復(fù)制算法//定義&#xff1a;將內(nèi)存分兩塊&#xff0c;每使用一塊&#xff0c;都會在內(nèi)存用完之后&#xff0c;將存活的對象復(fù)制到另一塊中&#xff0c;再把使用過的空間清理//問題&#xff1a;浪費(fèi)空間&#xff0c;永遠(yuǎn)浪費(fèi)一半空間2、標(biāo)記清除算法//定義&#xff1a;標(biāo)記存活對象&#xff0c;統(tǒng)一回收未被標(biāo)記的對象//問題&#xff1a;1、效率問題&#xff1a;效率不高&#xff0c;對象過多&#xff0c;就要浪費(fèi)時(shí)間標(biāo)記對象	   2、空間問題&#xff1a;產(chǎn)生大量的不連續(xù)的碎片3、標(biāo)記整理算法
    跟標(biāo)記清除一樣&#xff0c;多了個整理存活對象的過程4、分代收集算法
    年輕代復(fù)制算法&#xff0c;老年代標(biāo)記整理

1.14、CMS(標(biāo)記-清除算法=>寫屏障 + 增量更新)

1.14.1、運(yùn)作過程

1、初始標(biāo)記&#xff1a; 暫停所有的其他線程(STW)&#xff0c;并記錄下gc roots直接能引用的對象&#xff0c;速度很快。2、并發(fā)標(biāo)記&#xff1a; 并發(fā)標(biāo)記階段就是從GC Roots的直接關(guān)聯(lián)對象開始遍歷整個對象圖的過程&#xff0c; 這個過程耗時(shí)較長但是不需要停頓用戶線程&#xff0c; 可以與垃圾收集線程一起并發(fā)運(yùn)行。因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行&#xff0c;可能會有導(dǎo)致已經(jīng)標(biāo)記過的對象狀態(tài)發(fā)生改變。3、重新標(biāo)記&#xff1a; 重新標(biāo)記階段就是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄&#xff0c;這個階段的停頓時(shí)間一般會比初始標(biāo)記階段的時(shí)間稍長&#xff0c;遠(yuǎn)遠(yuǎn)比并發(fā)標(biāo)記階段時(shí)間短。主要用到'增量更新算法'做重新標(biāo)記。4、并發(fā)清理&#xff1a; 開啟用戶線程&#xff0c;同時(shí)GC線程開始對未標(biāo)記的區(qū)域做清掃。這個階段如果有新增對象會被標(biāo)記為`三色標(biāo)記法`里面的黑色不做任何處理5、并發(fā)重置&#xff1a;重置本次GC過程中的標(biāo)記數(shù)據(jù)。

1.14.2、三色標(biāo)記法

黑色&#xff1a;'表示對象已經(jīng)被垃圾收集器訪問過'&#xff0c;且這個對象的所有引用都已經(jīng)掃描過。黑色的對象代表已經(jīng)掃描過&#xff0c; 它是安全存活的&#xff0c;如果有其他對象引用指向了黑色對象&#xff0c;無須重新掃描一遍。黑色對象不可能直接&#xff08;不經(jīng)過灰色對象&#xff09;指向某個白色對象。
灰色&#xff1a;'表示對象已經(jīng)被垃圾收集器訪問過'&#xff0c;但這個對象上至少存在一個引用還沒有被掃描過。
白色&#xff1a;'表示對象尚未被垃圾收集器訪問過'。顯然在可達(dá)性分析剛剛開始的階段&#xff0c;所有的對象都是白色的&#xff0c;若在分析結(jié)束的階段&#xff0c;仍然是白色的對象&#xff0c;即代表不可達(dá)。

1.14.3、漏標(biāo)-讀寫屏障(解決方案)

1、增量更新(Incremental Update)+寫屏障
   
    增量更新就是當(dāng)黑色對象插入新的指向白色對象的引用關(guān)系時(shí)&#xff0c; 就將這個新插入的引用記錄下來&#xff0c; 等并發(fā)掃描結(jié)束之后&#xff0c; 再將這些記錄過的引用關(guān)系中的黑色對象為根&#xff0c; 重新掃描一次。 這可以簡化理解為&#xff0c; 黑色對象一旦新插入了指向白色對象的引用之后&#xff0c; 它就變回灰色對象了。2、原始快照&#xff08;Snapshot At The Beginning&#xff0c;SATB&#xff09;+寫屏障
    原始快照就是當(dāng)灰色對象要刪除指向白色對象的引用關(guān)系時(shí)&#xff0c; 就將這個要刪除的引用記錄下來&#xff0c; 在并發(fā)掃描結(jié)束之后&#xff0c;再將這些記錄過的引用關(guān)系中的灰色對象為根&#xff0c; 重新掃描一次&#xff0c;這樣就能掃描到白色的對象&#xff0c;將白色對象直接標(biāo)記為黑色(目的就是讓這種對象在本輪gc清理中能存活下來&#xff0c;待下一輪gc的時(shí)候重新掃描&#xff0c;這個對象也有可能是浮動垃圾)以上無論是對引用關(guān)系記錄的插入還是刪除&#xff0c; 虛擬機(jī)的記錄操作都是通過寫屏障實(shí)現(xiàn)的。

1.5、G1&#xff08;復(fù)制算法=>寫屏障 + SATB&#xff09;

1.5.1、運(yùn)作過程

初始標(biāo)記&#xff08;initial mark&#xff0c;STW&#xff09;&#xff1a;暫停所有的其他線程&#xff0c;并記錄下gc roots直接能引用的對象&#xff0c;速度很快 
并發(fā)標(biāo)記&#xff08;Concurrent Marking&#xff09;&#xff1a;并發(fā)標(biāo)記階段就是從GC Roots的直接關(guān)聯(lián)對象開始遍歷整個對象圖的過程&#xff0c; 這個過程耗時(shí)較長但是不需要停頓用戶線程&#xff0c; 可以與垃圾收集線程一起并發(fā)運(yùn)行。因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行&#xff0c;可能會有導(dǎo)致已經(jīng)標(biāo)記過的對象狀態(tài)發(fā)生改變。
最終標(biāo)記&#xff08;Remark&#xff0c;STW&#xff09;&#xff1a;重新標(biāo)記階段就是為了修正并發(fā)標(biāo)記期間因?yàn)橛脩舫绦蚶^續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄&#xff0c;這個階段的停頓時(shí)間一般會比初始標(biāo)記階段的時(shí)間稍長&#xff0c;遠(yuǎn)遠(yuǎn)比并發(fā)標(biāo)記階段時(shí)間短。主要用到'增量更新算法'做重新標(biāo)記。
篩選回收&#xff08;Cleanup&#xff0c;STW&#xff09;&#xff1a;篩選回收階段首先對各個Region的==回收價(jià)值和成本進(jìn)行排序&#xff0c;根據(jù)用戶所期望的GC停頓時(shí)間(可以用JVM參數(shù) -XX:MaxGCPauseMillis指定)來制定回收計(jì)劃

1.5.2、G1的RS和CT

'已記憶集合RememberedSets&#xff1a;'&#xff0c;
    存儲著其他分區(qū)中的對象對本分區(qū)對象的引用,每個分區(qū)有且只有一個RSet。用于提高GC效率。
YGC時(shí)&#xff0c;GC root主要是兩類&#xff1a;棧空間和老年代分區(qū)到新生代分區(qū)的引用關(guān)系。所以記錄老年代分區(qū)對新生代分區(qū)的引用
Mixed GC時(shí)&#xff0c;由于僅回收部分老年代分區(qū)&#xff0c;老年代分區(qū)之間的引用關(guān)系也將被使用。所以記錄老年代分區(qū)之間的引用
因此&#xff0c;我們僅需要記錄兩種引用關(guān)系&#xff1a;老年代分區(qū)引用新生代分區(qū)&#xff0c;老年代分區(qū)之間的引用。
因?yàn)槊看蜧C都會掃描所有young區(qū)對象&#xff0c;所以RSet只有在掃描old引用young&#xff0c;old引用old時(shí)會被使用。'卡表,Card Table&#xff1a;'Java堆劃分為相等大小的一個個區(qū)域&#xff0c;這個小的區(qū)域&#xff08;一般size在128-512字節(jié)&#xff09;被當(dāng)做Card&#xff0c;而Card Table維護(hù)著所有的Card。Card Table的結(jié)構(gòu)是一個字節(jié)數(shù)組,Card Table用單字節(jié)的信息映射著一個Card。當(dāng)Card中存儲了對象時(shí)&#xff0c;稱為這個Card被臟化了&#xff08;dirty card&#xff09;。 對于一些熱點(diǎn)Card會存放到Hot card cache。同Card Table一樣&#xff0c;Hot card cache也是全局的結(jié)構(gòu)。

1.5.3、Collect Set

Collect Set(CSet)是指&#xff0c;在Evacuation階段&#xff0c;由G1垃圾回收器選擇的待回收的Region集合。G1垃圾回收器的軟實(shí)時(shí)的特性就是通過CSet的選擇來實(shí)現(xiàn)的。對應(yīng)于算法的兩種模式fully-young generational mode和partially-young mode&#xff0c;CSet的選擇可以分成兩種&#xff1a;
    在fully-young generational mode下&#xff1a;顧名思義&#xff0c;該模式下CSet將只包含young的Region。G1將調(diào)整young的Region的數(shù)量來匹配軟實(shí)時(shí)的目標(biāo)&#xff1b;
    在partially-young mode下&#xff1a;該模式會選擇所有的young region&#xff0c;并且選擇一部分的old region。old region的選擇將依據(jù)在Marking cycle phase中對存活對象的計(jì)數(shù)。G1選擇存活對象最少的Region進(jìn)行回收。

1.5.4、Marking bitmaps/TAMS

Marking bitmap是一種數(shù)據(jù)結(jié)構(gòu)&#xff0c;其中的每一個bit代表的是一個可用于分配給對象的起始地址
bitmap
其中addrN代表的是一個對象的起始地址。綠色的塊代表的是在該起始地址處的對象是存活對象&#xff0c;而其余白色的塊則代表了垃圾對象。
G1使用了兩個bitmap&#xff0c;一個叫做previous bitmap&#xff0c;另外一個叫做next bitmap。previous bitmap記錄的是上一次的標(biāo)記階段完成之后的構(gòu)造的bitmap&#xff1b;next bitmap則是當(dāng)前正在標(biāo)記階段正在構(gòu)造的bitmap。在當(dāng)前標(biāo)記階段結(jié)束之后&#xff0c;當(dāng)前標(biāo)記的next bitmap就變成了下一次標(biāo)記階段的previous bitmap。TAMS(top at mark start)變量&#xff0c;是一對用于區(qū)分在標(biāo)記階段新分配對象的變量&#xff0c;分別被稱為previous TAMS和next TAMS。在previous TAMS和next TAMS之間的對象則是本次標(biāo)記階段時(shí)候新分配的對象。
previous TMAS 和 next TAMS
白色region代表的是空閑空間&#xff0c;綠色region代表是存活對象&#xff0c;橙色region代表的在此次標(biāo)記階段新分配的對象。注意的是&#xff0c;在橙色區(qū)域的對象&#xff0c;并不能確保它們都事實(shí)上是存活的。

1.11、ZGC的顏色指針

組成

GC信息保存在指針中。
每個對象有一個64位指針&#xff0c;這64位被分為18位&#xff1a;預(yù)留給以后使用1位&#xff1a;Finalizable標(biāo)識&#xff0c;此位與并發(fā)引用處理有關(guān)&#xff0c;它表示這個對象只能通過finalizer才能訪問1位&#xff1a;Remapped標(biāo)識&#xff0c;設(shè)置此位的值后&#xff0c;對象未指向relocation set中&#xff08;relocation set表示需要GC的Region集合&#xff09;1位&#xff1a;Marked1標(biāo)識1位&#xff1a;Marked0標(biāo)識&#xff0c;和上面的Marked1都是標(biāo)記對象用于輔助GC42位&#xff1a;對象的地址&#xff08;所以它可以支持2^42=4T內(nèi)存&#xff09;

優(yōu)勢&#xff1a;

一旦某個Region的存活對象被移走之后&#xff0c;這個Region立即就能夠被釋放和重用掉&#xff0c;而不必等待整個堆中所有指向該Region的引用都被修正后才能清理&#xff0c;這使得理論上只要還有一個空閑Region&#xff0c;ZGC就能完成收集。
顏色指針可以大幅減少在垃圾收集過程中內(nèi)存屏障的使用數(shù)量&#xff0c;ZGC只使用了讀屏障。
顏色指針具備強(qiáng)大的擴(kuò)展性&#xff0c;它可以作為一種可擴(kuò)展的存儲結(jié)構(gòu)用來記錄更多與對象標(biāo)記、重定位過程相關(guān)的數(shù)據(jù)&#xff0c;以便日后進(jìn)一步提高性能。

1.12、100%CPU排查

1使用top命令查看cpu占用資源較高的PID2、通過jps 找到當(dāng)前用戶下的java程序PID&#xff08;jps -l 能夠打印出所有的應(yīng)用的PID&#xff09;3、使用 pidstat -p4、找到cpu占用較高的線程TID5、將TID轉(zhuǎn)換為十六進(jìn)制的表示方式6、通過jstack -l&#xff08;使用jstack 輸出當(dāng)前PID的線程dunp信息&#xff09;7、 查找 TID對應(yīng)的線程(輸出的線程id為十六進(jìn)制)&#xff0c;找到對應(yīng)的代碼

1.13、JIT

JIT是一種提高程序運(yùn)行效率的方法。通常&#xff0c;程序有兩種運(yùn)行方式&#xff1a;靜態(tài)編譯與動態(tài)解釋。靜態(tài)編譯的程序在執(zhí)行前全部被翻譯為機(jī)器碼&#xff0c;而動態(tài)解釋執(zhí)行的則是一句一句邊運(yùn)行邊翻譯。

1.14、逃逸分析

逃逸分析是指在某個方法之內(nèi)創(chuàng)建的對象&#xff0c;除了在方法體之內(nèi)被引用之外&#xff0c;還在方法體之外被其它變量引用到&#xff1b;這樣帶來的后果是在該方法執(zhí)行完畢之后&#xff0c;該方法中創(chuàng)建的對象將無法被GC回收&#xff0c;由于其被其它變量引用。正常的方法調(diào)用中&#xff0c;方法體中創(chuàng)建的對象將在執(zhí)行完畢之后&#xff0c;將回收其中創(chuàng)建的對象&#xff1b;故由于無法回收&#xff0c;即成為逃逸。

2、Disruptor的原理&#xff08;生產(chǎn)消費(fèi)者模型&#xff09;

2.1、解決隊(duì)列速度慢

1、環(huán)形數(shù)組結(jié)構(gòu)&#xff1a;
	為了避免垃圾回收&#xff0c;采用數(shù)組而非鏈表。同時(shí)&#xff0c;數(shù)組對處理器的緩存機(jī)制更加友好&#xff08;CPU加載空間局部性原則&#xff09;。2、元素位置定位&#xff1a;
	數(shù)組長度2^n&#xff0c;通過位運(yùn)算&#xff0c;加快定位的速度。下標(biāo)采取遞增的形式。不用擔(dān)心index溢出的問題。index是long類型&#xff0c;即使100萬QPS的處理速度&#xff0c;也需要30萬年才能用完。3、無鎖設(shè)計(jì)&#xff1a;
	每個生產(chǎn)者或者消費(fèi)者線程&#xff0c;會先申請可以操作的元素在數(shù)組中的位置&#xff0c;申請到之后&#xff0c;直接在該位置寫入或者讀取數(shù)據(jù)

2.2、數(shù)據(jù)結(jié)構(gòu)

框架使用RingBuffer來作為隊(duì)列的數(shù)據(jù)結(jié)構(gòu)&#xff0c;RingBuffer就是一個可自定義大小的環(huán)形數(shù)組。除數(shù)組外還有一個序列號(sequence)&#xff0c;用以指向下一個可用的元素&#xff0c;供生產(chǎn)者與消費(fèi)者使用

2.3、核心組件

1、RingBuffer——Disruptor底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)&#xff0c;核心類&#xff0c;是線程間交換數(shù)據(jù)的中轉(zhuǎn)地&#xff1b;2、Sequencer——序號管理器&#xff0c;生產(chǎn)同步的實(shí)現(xiàn)者&#xff0c;負(fù)責(zé)消費(fèi)者/生產(chǎn)者各自序號、序號柵欄的管理和協(xié)調(diào),Sequencer有單生產(chǎn)者,多生產(chǎn)者兩種不同的模式,里面實(shí)現(xiàn)了各種同步的算法&#xff1b;3、Sequence——序號&#xff0c;聲明一個序號&#xff0c;用于跟蹤ringbuffer中任務(wù)的變化和消費(fèi)者的消費(fèi)情況&#xff0c;disruptor里面大部分的并發(fā)代碼都是通過對Sequence的值同步修改實(shí)現(xiàn)的,而非鎖,這是disruptor高性能的一個主要原因&#xff1b;4、SequenceBarrier——序號柵欄&#xff0c;管理和協(xié)調(diào)生產(chǎn)者的游標(biāo)序號和各個消費(fèi)者的序號&#xff0c;確保生產(chǎn)者不會覆蓋消費(fèi)者未來得及處理的消息&#xff0c;確保存在依賴的消費(fèi)者之間能夠按照正確的順序處理&#xff0c; Sequence Barrier是由Sequencer創(chuàng)建的,并被Processor持有&#xff1b;5、EventProcessor——事件處理器&#xff0c;監(jiān)聽RingBuffer的事件&#xff0c;并消費(fèi)可用事件&#xff0c;從RingBuffer讀取的事件會交由實(shí)際的生產(chǎn)者實(shí)現(xiàn)類來消費(fèi)&#xff1b;它會一直偵聽下一個可用的號&#xff0c;直到該序號對應(yīng)的事件已經(jīng)準(zhǔn)備好。6、EventHandler——業(yè)務(wù)處理器&#xff0c;是實(shí)際消費(fèi)者的接口&#xff0c;完成具體的業(yè)務(wù)邏輯實(shí)現(xiàn)&#xff0c;第三方實(shí)現(xiàn)該接口&#xff1b;代表著消費(fèi)者。7、Producer——生產(chǎn)者接口&#xff0c;第三方線程充當(dāng)該角色&#xff0c;producer向RingBuffer寫入事件。8、Wait Strategy&#xff1a;Wait Strategy決定了一個消費(fèi)者怎么等待生產(chǎn)者將事件&#xff08;Event&#xff09;放入Disruptor中。

2.4、Wait Strategy

1、BlockingWaitStrategy
	Disruptor的默認(rèn)策略是BlockingWaitStrategy。在BlockingWaitStrategy內(nèi)部是使用鎖和condition來控制線程的喚醒。BlockingWaitStrategy是最低效的策略&#xff0c;但其對CPU的消耗最小并且在各種不同部署環(huán)境中能提供更加一致的性能表現(xiàn)。2、SleepingWaitStrategy
	SleepingWaitStrategy 的性能表現(xiàn)跟 BlockingWaitStrategy 差不多&#xff0c;對 CPU 的消耗也類似&#xff0c;但其對生產(chǎn)者線程的影響最小&#xff0c;通過使用LockSupport.parkNanos(1)來實(shí)現(xiàn)循環(huán)等待。一般來說Linux系統(tǒng)會暫停一個線程約60μs&#xff0c;這樣做的好處是&#xff0c;生產(chǎn)線程不需要采取任何其他行動就可以增加適當(dāng)?shù)挠?jì)數(shù)器&#xff0c;也不需要花費(fèi)時(shí)間信號通知條件變量。但是&#xff0c;在生產(chǎn)者線程和使用者線程之間移動事件的平均延遲會更高。它在不需要低延遲并且對生產(chǎn)線程的影響較小的情況最好。一個常見的用例是異步日志記錄。3、YieldingWaitStrategy
	YieldingWaitStrategy是可以使用在低延遲系統(tǒng)的策略之一。YieldingWaitStrategy將自旋以等待序列增加到適當(dāng)?shù)闹怠T谘h(huán)體內(nèi)&#xff0c;將調(diào)用Thread.yield&#xff08;&#xff09;&#xff0c;以允許其他排隊(duì)的線程運(yùn)行。在要求極高性能且事件處理線數(shù)小于 CPU 邏輯核心數(shù)的場景中&#xff0c;推薦使用此策略&#xff1b;例如&#xff0c;CPU開啟超線程的特性。4、BusySpinWaitStrategy
	性能最好&#xff0c;適合用于低延遲的系統(tǒng)。在要求極高性能且事件處理線程數(shù)小于CPU邏輯核心數(shù)的場景中&#xff0c;推薦使用此策略&#xff1b;例如&#xff0c;CPU開啟超線程的特性。

2.5、寫數(shù)據(jù)

單線程寫數(shù)據(jù)的流程&#xff1a;	1、申請寫入m個元素&#xff1b;	2、若是有m個元素可以入&#xff0c;則返回最大的序列號。這兒主要判斷是否會覆蓋未讀的元素&#xff1b;	3、若是返回的正確&#xff0c;則生產(chǎn)者開始寫入元素。

3、Spring

3.1、Spring流程(IOC下的Bean的生命周期&#xff0c;循環(huán)依賴&#xff0c;構(gòu)造函數(shù))

1. 啟動ApplicationContext
    兩個重要的子類&#xff1a;    	AnnotationConfigApplicationContext(用的最多)	ClassPathXmlApplicationContext2. 初始化AnnotationBeanDefinitionReader
	a.讀取spring內(nèi)部的初始的 beanFactoryPostProcess 和 其他的幾種 beanPostProcess&#xff08;AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)&#xff09;		1. AnnotationAwareOrderComparator&#xff1a;解析@Order進(jìn)行排序		2. ContextAnnotationAutowireCandidateResolver		3. ConfigurationClassPostProcessor&#xff1a;解析加了@Configuration、@ComponentScan、@ComponentScans、@Import等注解&#xff08;最重要的類&#xff09;		4. AutowiredAnnotationBeanPostProcessor&#xff1a;解析@Autowired5. RequiredAnnotationBeanPostProcessor&#xff1a;解析@Required6. CommonAnnotationBeanPostProcessor&#xff1a;負(fù)責(zé)解析@Resource、@WebServiceRef、@EJB7. EventListenerMethodProcessor&#xff1a;找到@EventListener8. DefaultEventListenerFactory&#xff1a;解析@EventListener
	b. 在ConfigurationClassPostProcessor類中有主要是為了解析加了@Configuration、@ComponentScan、@ComponentScans、@Import等注解&#xff0c;在這里面他有一個細(xì)節(jié)&#xff0c;就是加了@Configuration里面&#xff0c;他會把當(dāng)前類標(biāo)注成full類&#xff0c;就會產(chǎn)生一個aop的動態(tài)代理去加載當(dāng)前類&#xff0c;沒有的話就把當(dāng)前類標(biāo)注成lite類&#xff0c;也就是普通類處理。3. 初始化ClassPathBeanDefinitionScanner
	a. 程序員能夠在外部調(diào)用doScan()&#xff0c; 或者 繼承該類可以重寫scan規(guī)則用來動態(tài)掃描注解&#xff0c;需要注冊到容器。
	b. spring內(nèi)部是自己重新new 新的對象來掃描。4. 執(zhí)行register()方法&#xff0c;一般來說就是注冊我們的配置類
	a. 先把此實(shí)體類型轉(zhuǎn)換為一個BeanDefinition5. 執(zhí)行refresh()&#xff0c;先初始化比如BeanFactory這類基礎(chǔ)的容器。
	a. 執(zhí)行invokeBeanFactoryPostProcessors()&#xff0c;主要的作用是掃描包和parse (類->beanDefinition)1. 執(zhí)行BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor方法postProcessBeanDefinitionRegistry&#xff08;BeanDefinitionRegistry register&#xff09;
			作用&#xff1a;主要是掃描包找到合格的類&#xff0c;解析類
				i. 先執(zhí)行程序員通過 context.add的
     		   ii. 再執(zhí)行spring內(nèi)部的和程序員通過注解注冊的 并且特殊的比如 實(shí)現(xiàn)了PriorityOrdered&#xff0c;Order
			  iii. 最后再執(zhí)行其他的 BeanDefinitionRegistryPostProcessor     	2. 再執(zhí)行BeanFactoryPostProcessor接口 方法postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)**作用&#xff1a;1. 和子接口一樣 掃描包找到合格的類&#xff0c;解析類 
      			 2. 為@Configuration的類做代理
                    i. 先執(zhí)行子接口中的方法
                   ii. 再執(zhí)行程序員通過 context.add添加的
                  iii. 再執(zhí)行spring內(nèi)部和程序員通過注解注冊的 并且特殊的比如 PriorityOrdered&#xff0c;Order
                   iv. 最后執(zhí)行其他的 BeanFactoryPostProcessor
		他們在spring中唯一的實(shí)現(xiàn)類是ConfigurationClassPostProcessor
			將類變成beanDefinition的流程:1. 從BeanDefinitionRegistry中獲取所有的bd2. 判斷是否該bd是否被解析過&#xff0c;主要根據(jù)bd中是否有full或者lite屬性。3. 將未解的bd去&#xff0c;循環(huán)解析bd
					a. 先處理內(nèi)部類
					b. 處理@PropertrySource 環(huán)境配置
					c. 處理@ComponentScan
						解析帶有ComponentScan&#xff0c;會調(diào)用ClassPathBeanDefinitionScanner&#xff0c;根據(jù)包路徑&#xff0c;和匹配規(guī)則掃描出合格類。
					d. 處理@Import
						i. 先處理 ImportSelect&#xff0c;執(zhí)行selectImports(), 事務(wù)的初始化和aop的代理類型&#xff0c;是否傳遞代理 就是在這里做的。
					   ii. 然后處理 ImportBeanDefinitionRegistrar接口&#xff0c;會放到該bd的一個Map中&#xff0c;循環(huán)map統(tǒng)一去執(zhí)行實(shí)現(xiàn)方法registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);  iii. 最后處理普通的類&#xff0c;同樣會遞歸去解析該bd
                    e. 處理@ImportResourcef. 處理@Bean
					g.處理接口bd4. 然后將所有的合格的類&#xff0c;轉(zhuǎn)換成bd&#xff0c;注冊到beanDefinitionRegistry。
	b. 然后會注冊beanPostProcessor&#xff0c;國際化等等&#xff0c;不是很重要
	c. 比較重要的&#xff0c;也是將bd變成bean的方法 finishBeanFactoryInitialization()&#xff0c;實(shí)例化非延遲的單例&#xff08;循環(huán)依賴&#xff09;
	d. 一般來說首先getBeanDefinition之前&#xff0c;都要合并bd。			1)第一次getSingleton&#xff0c;從單例池拿是否存在&#xff0c;單例的第一次一般是不存在&#xff0c;并且會判斷是否在正在創(chuàng)建bean的set集合中。
			  singletonObjects 一級緩存&#xff0c;完整的bean
			  singletonFactories 二級緩存&#xff0c;存的是代理bean工廠
			  earlySingletonObjects 三級緩存&#xff0c;一般是是半成品的bean
			  a. 如果存在&#xff0c;直接返回
			  b. 如果不存在&#xff0c;并且不在正在創(chuàng)建bean的set集合中&#xff0c;直接返回null
			  c. 如果不存在&#xff0c;并且在正在創(chuàng)建bean的set集合中。從三級緩存拿。
				i. 存在&#xff0c;直接三級緩存拿。
				ii. 不存在&#xff0c;通過二級緩存&#xff0c;代理的bean工廠拿&#xff0c;獲得該bean&#xff0c;然后將得到bean放到三級緩存中&#xff0c;移出二級緩存。(原因是生產(chǎn)bean工廠周期比較長的。)2)第二次getSingleton
			  a. 首先將beanName放到正在創(chuàng)建bean的set集合中&#xff0c;表示正在創(chuàng)建該bean
			  b. 然后會調(diào)用二級緩存去獲取bean&#xff0c;lambda延遲機(jī)制&#xff0c;就會調(diào)用表達(dá)式中&#xff0c;也就是createBean&#xff0c;這時(shí)候是正在獲取代理bean工廠會走一個完整的bean 的生命周期。
			  c. 然后從bean工廠獲取bean。	1. 構(gòu)造函數(shù)&#xff1a;第一次 BeanPostProcessor&#xff0c;是否需要代理bean。如果代理bean直接返回&#xff0c;不會走下面的流程。	2. 第二次BeanPostProcessor&#xff0c;推斷構(gòu)造函數(shù)
		a. 首先推斷構(gòu)造函數(shù)數(shù)組
			i. 沒提供構(gòu)造函數(shù)=========設(shè)置構(gòu)造函數(shù)數(shù)組為null
		   ii. 一個默認(rèn)的構(gòu)造函數(shù)======設(shè)置構(gòu)造函數(shù)數(shù)組為null
          iii. 一個不是默認(rèn)的構(gòu)造函數(shù)===設(shè)置構(gòu)造函數(shù)數(shù)組為該構(gòu)造函數(shù)
		   iv. 一個構(gòu)造方法并且加了@Autowired====設(shè)置構(gòu)造函數(shù)數(shù)組為該構(gòu)造函數(shù)
			v. 多個模糊構(gòu)造函數(shù)========設(shè)置構(gòu)造函數(shù)數(shù)組為null
		   vi. 多個構(gòu)造函數(shù)&#xff0c;有唯一加了@Autowired==設(shè)置構(gòu)造函數(shù)數(shù)組為該構(gòu)造函數(shù)
		  vii. 多個構(gòu)造函數(shù)&#xff0c;多個@Autowired&#xff08;required為false&#xff09;===設(shè)置構(gòu)造函數(shù)數(shù)組為多個@Autowired
		 viii. 提供多個構(gòu)造函數(shù)&#xff0c;多個@Autowired&#xff08;required為true&#xff09;=== 拋異常
     	b. 如果推斷構(gòu)造數(shù)組不為null 或者&#xff0c;自動注入類型為構(gòu)造函數(shù)&#xff0c;或者設(shè)置了構(gòu)造函數(shù)的屬性&#xff08;xml方式&#xff09;等&#xff0c;還有一種傳參數(shù)金來
			i. 推斷構(gòu)造函數(shù)&#xff0c;				1. 只有個構(gòu)造函數(shù)&#xff0c;最終被確定的構(gòu)造函數(shù)&#xff0c;2. 有多個構(gòu)造函數(shù)
     				a. 優(yōu)先修飾符最開放的&#xff0c;public>protected>Default>private 				b. 修飾符一樣找屬性最多的
      	   ii. 推斷參數(shù)&#xff0c;				1. 首先找出所有候選的參數(shù)類型&#xff0c;實(shí)例化屬性				2. 然后類型是接口&#xff0c;那么判斷是否開啟寬松構(gòu)造
					a. 未開啟報(bào)錯。
					b. 開啟了&#xff0c;判斷子類的差值&#xff08;spring有個算法&#xff09;&#xff0c;默認(rèn)差值是-1024。
					c. 差值低的為該參數(shù)&#xff0c;一樣的丟到模糊集合中&#xff0c;隨機(jī)取出。

     	c. 構(gòu)造函數(shù)數(shù)組為null&#xff0c;直接通過無參實(shí)例化構(gòu)造函數(shù)。   	3. 第三次BeanPostProcessor ,緩存了注入元素的信息
         injectionMetadataCache:  key: beanName或者類名  value&#xff1a;為解析出的屬性&#xff08;包括方法&#xff09;集合 InjectionMetadata。
         InjectionMetadata&#xff1a;可以存放method 和 屬性。類中有字段判斷是否是屬性 isField。
         checkedInitMethods&#xff1a; 存放  @PostConstruct 。
         checkedDestroyMethods&#xff1a;存放 @PreDestroy。
            a. AutowiredAnnotationBeanPostProcessor  主要解析加了 @Autowired 和 @Value 方法和屬性。
            b. CommonAnnotationBeanPostProcessor  主要解析加了 @Resource屬性。
            c. InitDestroyAnnotationBeanPostProcessor 主要解析加了 @PostConstruct 和 @PreDestroy方法
            d. 還有很多   	4. 第四次 BeanPostProcessor&#xff0c;生產(chǎn)代理工廠&#xff0c;作用是可以解決循環(huán)依賴
      	a. 先判斷是否允許循環(huán)依賴&#xff0c;可通過api修改屬性&#xff0c;或者直接改源代碼。
      	b. 然后判斷當(dāng)前bean是否是正在創(chuàng)建的bean
       	c. 調(diào)用populateBean 主要作用&#xff0c;注入屬性。	5. 第五次BeanPostProcessor&#xff0c;控制是否需要屬性注入&#xff0c;目前沒什么作用。
     	再注入緩存的屬性之前&#xff0c;先通過 自動注入模型
       		a. byType byName&#xff0c;找到setter,注入。體現(xiàn)了@Autowired不是自動注入&#xff0c;而是手動注入。6. 第六次 BeanPostProcessor &#xff0c;完成注解的屬性填充** **@Autowired @Resource**  	a. 注入之前還是會再找一下是否有其他需要注入的屬性和方法。
     	b. 屬性的調(diào)用屬性注入方法&#xff0c;函數(shù)調(diào)用函數(shù)的注入方法。
     		i. 通過屬性的類型&#xff0c;從BeanDefinitionMap中找屬性名稱(接口則找找這個接口的子類), 	   ii. 然后判斷我們當(dāng)前需要注入的屬性是不是這幾個類型&#xff0c;得到候選的類型。
		  iii. 當(dāng)有多個類型&#xff0c;再通過屬性名稱去推斷出唯一候選的屬性名。如果找到多個候選的屬性名&#xff0c;拋異常。
		   iv. 只有唯一的屬性名&#xff0c;通過類名去獲取類型。
			v. 最終通過找到唯一匹配的beanName和類型去注入。當(dāng)沒有找到匹配的名稱和類型&#xff0c;就會拋異常。
     	c. 在注入的時(shí)候&#xff0c;有循環(huán)依賴的時(shí)候&#xff0c;會去先去實(shí)例化該屬性。   	7. 第七次BeanPostProcessor &#xff0c;處理實(shí)現(xiàn)各種aware接口的重寫方法 + 生命周期回調(diào) 執(zhí)行@PostConstruct方法
           執(zhí)行 實(shí)現(xiàn)InitializingBean接口的&#xff0c;重寫方法&#xff0c;和  xml 中的 init-method="xxx"方法。 8. 第八次BeanPostProcessor &#xff0c;做aop代理
		a. 判斷是否需要做代理
			i. 找出所有的候選切面&#xff0c;比如 加了 @Aspect的類 &#xff0c; 事務(wù)的切面
		   ii. 做匹配邏輯&#xff0c;比如根據(jù)切面的連接點(diǎn)表達(dá)式 或者 類中方法是否加了@Transaction去 判斷當(dāng)前類是否匹配出&#xff0c;合適的切面集合。
		  iii. 然后對匹配出的切面集合&#xff0c;做排序。
		   iv. 能匹配上說明就做代理
		b. 哪種代理&#xff08;默認(rèn)用JDK動態(tài)代理&#xff09;
			i. 當(dāng)代理工廠設(shè)置ProxyTargetClass為 true&#xff0c;則為CGLIB代理。
		   ii. 當(dāng)目標(biāo)對象為類&#xff0c;則也用為CGLIB代理。
		  iii. 只有proxyTarget為 false&#xff0c;并且為目標(biāo)對象為接口&#xff0c;則用JDK動態(tài)代理
		c. 執(zhí)行代理invokeHandler&#xff08;這里主要是JDK的代理&#xff0c;invoke方法&#xff09;
			i. 首先會進(jìn)行普通方法的判斷比如hashcode eques等等&#xff0c;沒有就給代理類創(chuàng)建。不是很重要
		   ii. 然后判斷是否需要將代理傳遞下去&#xff0c;就是綁定到 ThreadLocal中&#xff08;在事務(wù)中&#xff0c;這個特別的重要&#xff09;
		  iii. 獲取執(zhí)行鏈&#xff0c;也就是這個目標(biāo)對象的通知集合。&#xff08;也就是所有過濾器鏈&#xff0c;實(shí)現(xiàn)了MethodIntercept。&#xff09;
		   iv. 執(zhí)行過濾器執(zhí)行鏈&#xff0c;類似于火炬?zhèn)鬟f。&#xff08;事務(wù)的methodInterceptor也在這里會被調(diào)用&#xff09;				1. 判斷通知是否執(zhí)行完&#xff0c;沒有執(zhí)行完去&#xff0c;按順序執(zhí)行通知。				2. 依次調(diào)用對應(yīng)的通知&#xff0c;最終都會去回調(diào)到proceed&#xff08;&#xff09;方法。				3. 最終執(zhí)行完代理方法&#xff0c;就會調(diào)用本身的方法。比較特殊的是around是在通知里&#xff0c;執(zhí)行被代理的目標(biāo)方法。

3.2、AOP

源碼底層的實(shí)現(xiàn)是動態(tài)代理
    動態(tài)代理有cglib和jdk實(shí)現(xiàn)1、JDK動態(tài)代理通過反射機(jī)制實(shí)現(xiàn)&#xff1a;
        通過實(shí)現(xiàn)InvocationHandlet接口創(chuàng)建自己的調(diào)用處理器&#xff1b;
        通過為Proxy類指定ClassLoader對象和一組interface來創(chuàng)建動態(tài)代理&#xff1b;
        通過反射機(jī)制獲取動態(tài)代理類的構(gòu)造函數(shù)&#xff0c;其唯一參數(shù)類型就是調(diào)用處理器接口類型&#xff1b;
        通過構(gòu)造函數(shù)創(chuàng)建動態(tài)代理類實(shí)例&#xff0c;構(gòu)造時(shí)調(diào)用處理器對象作為參數(shù)參入&#xff1b;
	JDK動態(tài)代理是面向接口的代理模式&#xff0c;如果被代理目標(biāo)沒有接口那么Spring也無能為力&#xff0c;Spring通過Java的反射機(jī)制生產(chǎn)被代理接口的新的匿名實(shí)現(xiàn)類&#xff0c;重寫了其中AOP的增強(qiáng)方法。	2、CGLib動態(tài)代理&#xff1a;
CGLib是一個強(qiáng)大、高性能的Code生產(chǎn)類庫&#xff0c;可以實(shí)現(xiàn)運(yùn)行期動態(tài)擴(kuò)展java類&#xff0c;Spring在運(yùn)行期間通過 CGlib繼承要被動態(tài)代理的類&#xff0c;重寫父類的方法&#xff0c;實(shí)現(xiàn)AOP面向切面編程&#xff0c;底層是ASM實(shí)現(xiàn)	3、兩者對比&#xff1a;
		JDK動態(tài)代理是面向接口的。
		CGLib動態(tài)代理是通過字節(jié)碼底層繼承要代理類來實(shí)現(xiàn)&#xff08;被代理類不能被final關(guān)鍵字所修飾&#xff0c;&#xff09;。	4、使用注意&#xff1a;
		如果要被代理的對象是個實(shí)現(xiàn)類&#xff0c;那么Spring會使用JDK動態(tài)代理來完成操作&#xff08;Spirng默認(rèn)采用JDK動態(tài)代理實(shí)現(xiàn)機(jī)制&#xff09;&#xff1b;
		如果要被代理的對象不是個實(shí)現(xiàn)類&#xff0c;那么Spring會強(qiáng)制使用CGLib來實(shí)現(xiàn)動態(tài)代理

4、SpringMVC

4.1、SpringMVC執(zhí)行流程

1&#xff09;前端控制器DispatcherServlet 由框架提供作用&#xff1a;接收請求&#xff0c;處理響應(yīng)結(jié)果 
2&#xff09;處理器映射器HandlerMapping由框架提供 
	作用&#xff1a;根據(jù)請求URL&#xff0c;找到對應(yīng)的Handler 
3&#xff09;處理器適配器HandlerAdapter由框架提供 
	作用&#xff1a;調(diào)用處理器&#xff08;Handler|Controller&#xff09;的方法 
4&#xff09;處理器Handler又名Controller,后端處理器 
	作用&#xff1a;接收用戶請求數(shù)據(jù)&#xff0c;調(diào)用業(yè)務(wù)方法處理請求 
5&#xff09;視圖解析器ViewResolver由框架提供 
	作用&#xff1a;視圖解析&#xff0c;把邏輯視圖名稱解析成真正的物理視圖 
	支持多種視圖技術(shù)&#xff1a;JSTLView,FreeMarker... 6&#xff09;視圖View,程序員開發(fā) 
	作用&#xff1a;將數(shù)據(jù)展現(xiàn)給用戶

5、SpringBoot

5.1、SpringBoot的自動裝箱

1、@SpringBootApplication=>2、@EnableAutoConfiguration=>3、@Import(AutoConfigurationImportSelector.class)=>調(diào)用getCandidateConfigurations()方法&#xff0c;里面有個讀取Meta-info/spring.factories

5.2、Starter自動裝配

1.編寫一個帶有@Configuration注解的類&#xff0c;如果按條件加載可以加上@ConditionalOnClass或@ConditionalOnBean注解2.在classpath下創(chuàng)建META-INF/spring.factories文件&#xff0c;并在spring.factories中添加
	org.springframework.boot.autoconfigure.EnableAutoConfiguretion =上面定義類的全類名

6、RabbitMQ

6.1、消息丟失

6.1.1、生產(chǎn)者丟失消息

RabbitMQ提供transaction和confirm模式來確保生產(chǎn)者不丟消息&#xff1b;
transaction機(jī)制就是說&#xff1a;發(fā)送消息前&#xff0c;開啟事務(wù)&#xff08;channel.txSelect()&#xff09;,然后發(fā)送消息&#xff0c;如果發(fā)送過程中出現(xiàn)什么異常&#xff0c;事務(wù)就會回滾&#xff08;channel.txRollback()&#xff09;,如果發(fā)送成功則提交事務(wù)&#xff08;channel.txCommit()&#xff09;。然而&#xff0c;這種方式有個缺點(diǎn)&#xff1a;吞吐量下降&#xff1b;
confirm模式用的居多&#xff1a;一旦channel進(jìn)入confirm模式&#xff0c;所有在該信道上發(fā)布的消息都將會被指派一個唯一的ID&#xff08;從1開始&#xff09;&#xff0c;一旦消息被投遞到所有匹配的隊(duì)列之后&#xff1b;
rabbitMQ就會發(fā)送一個ACK給生產(chǎn)者&#xff08;包含消息的唯一ID&#xff09;&#xff0c;這就使得生產(chǎn)者知道消息已經(jīng)正確到達(dá)目的隊(duì)列了&#xff1b;
如果rabbitMQ沒能處理該消息&#xff0c;則會發(fā)送一個Nack消息給你&#xff0c;你可以進(jìn)行重試操作。

6.1.2、消息隊(duì)列丟數(shù)據(jù)&#xff1a;消息持久化。

處理消息隊(duì)列丟數(shù)據(jù)的情況&#xff0c;一般是開啟持久化磁盤的配置。
這個持久化配置可以和confirm機(jī)制配合使用&#xff0c;你可以在消息持久化磁盤后&#xff0c;再給生產(chǎn)者發(fā)送一個Ack信號。
這樣&#xff0c;如果消息持久化磁盤之前&#xff0c;rabbitMQ陣亡了&#xff0c;那么生產(chǎn)者收不到Ack信號&#xff0c;生產(chǎn)者會自動重發(fā)。
那么如何持久化呢&#xff1f;1. 將queue的持久化標(biāo)識durable設(shè)置為true,則代表是一個持久的隊(duì)列2. 發(fā)送消息的時(shí)候?qū)eliveryMode=2這樣設(shè)置以后&#xff0c;即使rabbitMQ掛了&#xff0c;重啟后也能恢復(fù)數(shù)據(jù)

6.1.3、消費(fèi)者丟失消息&#xff1a;消費(fèi)者丟數(shù)據(jù)一般是因?yàn)椴捎昧俗詣哟_認(rèn)消息模式&#xff0c;改為手動確認(rèn)消息即可&#xff01;

消費(fèi)者在收到消息之后&#xff0c;處理消息之前&#xff0c;會自動回復(fù)RabbitMQ已收到消息&#xff1b;

如果這時(shí)處理消息失敗&#xff0c;就會丟失該消息&#xff1b;
    
解決方案&#xff1a;處理消息成功后&#xff0c;手動回復(fù)確認(rèn)消息。(手動ACK)

6.1.4、消息不被重復(fù)消費(fèi)(冪等性)

保證消息的唯一性&#xff0c;就算是多次傳輸&#xff0c;不要讓消息的多次消費(fèi)帶來影響&#xff1b;保證消息等冪性=》redis&#xff0c;數(shù)據(jù)庫自增

6.1.5、如何保證RabbitMQ消息的順序性&#xff1f;

同一個queue里面消息是有序的&#xff0c;保證消息發(fā)送到同一個queue就好了。
單線程消費(fèi)保證消息的順序性&#xff1b;對消息進(jìn)行編號&#xff0c;消費(fèi)者處理消息是根據(jù)編號處理消息&#xff1b;

7、Redis

7.1、Redis線程模型(reactor模型)

Redis的IO多路復(fù)用&#xff1a;redis利用epoll來實(shí)現(xiàn)IO多路復(fù)用&#xff0c;將連接信息和事件放到隊(duì)列中&#xff0c;依次放到
文件事件分派器&#xff0c;事件分派器將事件分發(fā)給事件處理器。

JVM+Redis+SpringBoot的面試題有哪些

7.2、Redis核心數(shù)據(jù)結(jié)構(gòu)使用與原理

7.2.1、String

1、底層&#xff1a;
    是SDS實(shí)現(xiàn)&#xff0c;其編碼方式有int&#xff0c;raw&#xff0c;embstr&#xff0c;主要存在于redisObject的ptr屬性中
        a. 默認(rèn)是int&#xff0c;正式類型是longb. 當(dāng)字符串大于32字節(jié)的字符串值&#xff0c;設(shè)置為raw
        c.當(dāng) 字符串保存的小于等于32字節(jié)&#xff0c;設(shè)置為embstr
    總結(jié)&#xff1a;
        在Redis中&#xff0c;存儲long、double類型的浮點(diǎn)數(shù)是先轉(zhuǎn)換為字符串再進(jìn)行存儲的。
        raw與embstr編碼效果是相同的&#xff0c;不同在于內(nèi)存分配與釋放&#xff0c;raw兩次&#xff0c;embstr一次。
        embstr內(nèi)存塊連續(xù)&#xff0c;能更好的利用緩存在來的優(yōu)勢int編碼和embstr編碼如果做追加字符串等操作&#xff0c;滿足條件下會被轉(zhuǎn)換為raw編碼&#xff1b;embstr編碼的對象是只讀的&#xff0c;一旦修改會先轉(zhuǎn)碼到raw。2、應(yīng)用場景
    a. 單值緩存
    b. 分布式鎖
    c. 計(jì)數(shù)器
    d. Web集群session共享
    e. 分布式系統(tǒng)全局序列號
    f. 對象緩存

7.2.2、List

1、底層&#xff1a;
	List是一個有序(按加入的時(shí)序排序)的數(shù)據(jù)結(jié)構(gòu)&#xff0c;Redis采用quicklist&#xff08;雙端鏈表&#xff09; 和 ziplist 作為List的底層實(shí)現(xiàn)2、應(yīng)用場景
    a.Stack(棧) = LPUSH + LPOP
	b. Queue(隊(duì)列&#xff09;= LPUSH + RPOP
	c. Blocking MQ(阻塞隊(duì)列&#xff09;= LPUSH + BRPOP
	d. 微博和微信公號消息流
	e. 微博消息和微信公號消息

7.2.3、Hash

1、底層&#xff1a;
    Hash 數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)為一個字典( dict ),也是RedisBb用來存儲K-V的數(shù)據(jù)結(jié)構(gòu),當(dāng)數(shù)據(jù)量比較小&#xff0c;或者單個元素比較小時(shí)&#xff0c;底層用ziplist存儲&#xff0c;數(shù)據(jù)大小和元素?cái)?shù)量閾值可以通過如下參數(shù)設(shè)置2、應(yīng)用場景&#xff1a;
    a. 對象緩存
	b. 電商購物車
	c. 購物車操作&#xff08;添加商品&#xff0c;增加數(shù)量&#xff0c;商品總數(shù)&#xff0c;刪除商品&#xff0c;獲取購物車所有商品&#xff09;

7.2.4、Set

1、底層&#xff1a;
	Set為無序的&#xff0c;自動去重的集合數(shù)據(jù)類型&#xff0c;Set數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)為一個value為null的字典( dict ),當(dāng)數(shù)據(jù)可以用整形表示時(shí)&#xff0c;Set集合將被編碼為intset數(shù)據(jù)結(jié)構(gòu)。兩個條件任意滿足時(shí)Set將用hashtable存儲數(shù)據(jù)。
    a. 元素個數(shù)大于 set-max-intset-entries ,b. 元素?zé)o法用整形表示
		set-max-intset-entries 512 // intset 能存儲的最大元素個數(shù)&#xff0c;超過則用hashtable編碼2、應(yīng)用場景&#xff1a;
    a. 微信抽獎小程序
    b. 微信微博點(diǎn)贊&#xff0c;收藏&#xff0c;標(biāo)簽
    c. 集合操作實(shí)現(xiàn)微博微信關(guān)注模型
    d. 集合操作實(shí)現(xiàn)電商商品篩選

7.2.5、Sort Set

1、底層&#xff1a;
Sort Set 為有序的&#xff0c;自動去重的集合數(shù)據(jù)類型&#xff0c;ZSet 數(shù)據(jù)結(jié)構(gòu)底層實(shí)現(xiàn)為 字典(dict) + 跳表(skiplist) ,當(dāng)數(shù)據(jù)比較少時(shí)&#xff0c;用ziplist編碼結(jié)構(gòu)存儲
    zset-max-ziplist-entries  128    // 元素個數(shù)超過128 &#xff0c;將用skiplist編碼
	zset-max-ziplist-value     64     //  單個元素大小超過 64 byte, 將用 skiplist編碼2、應(yīng)用場景&#xff1a;
    a. 點(diǎn)擊新聞
    b. 展示當(dāng)日排行前十
    c. 七日搜索榜單計(jì)算
    d. 展示七日排行前十

7.2.6、GeoHash

1、底層&#xff1a;
    空間填充曲線&#xff0c;也就是經(jīng)緯度換編碼&#xff0c;二分取右為1地球緯度區(qū)間是[-90,90]&#xff0c; 如某緯度是39.92324&#xff0c;可以通過下面算法來進(jìn)行維度編碼:1&#xff09;區(qū)間[-90,90]進(jìn)行二分為[-90,0),[0,90]&#xff0c;稱為左右區(qū)間&#xff0c;可以確定39.92324屬于右區(qū)間[0,90]&#xff0c;給標(biāo)記為12&#xff09;接著將區(qū)間[0,90]進(jìn)行二分為 [0,45),[45,90]&#xff0c;可以確定39.92324屬于左區(qū)間 [0,45)&#xff0c;給標(biāo)記為03&#xff09;遞歸上述過程39.92324總是屬于某個區(qū)間[a,b]。隨著每次迭代區(qū)間[a,b]總在縮小&#xff0c;并越來越逼近39.9281674&#xff09;如果給定的緯度&#xff08;39.92324&#xff09;屬于左區(qū)間&#xff0c;則記錄0&#xff0c;如果屬于右區(qū)間則記錄1&#xff0c;這樣隨著算法的進(jìn)行會 產(chǎn)生一個序列1011 1000 1100 0111 1001&#xff0c;序列的長度跟給定的區(qū)間劃分次數(shù)有關(guān)。2、應(yīng)用場景&#xff1a;
    搖一搖
    附近位置

7.2.7、BloomFilter

底層是取n個hash&#xff0c;做位運(yùn)算

7.2.8、HyperLogLog&#xff08;基數(shù)統(tǒng)計(jì)&#xff09;&#xff1a;統(tǒng)計(jì)用戶訪問量

7.3、持久化

RDB&#xff1a;快照&#xff0c;bgsave異步創(chuàng)建dump.rdb文件&#xff0c;底層是fork+cow實(shí)現(xiàn)。
AOF&#xff1a;追加&#xff0c;底層是先寫入緩存中&#xff0c;然后每隔一段時(shí)間會fsync到磁盤&#xff0c;也是fork一個子進(jìn)程
運(yùn)行&#xff1a;默認(rèn)加載rdb文件&#xff0c;如果同時(shí)啟用了RDB 和 AOF 方式&#xff0c;AOF 優(yōu)先&#xff0c;啟動時(shí)只加載 AOF 文件恢復(fù)數(shù)據(jù)&#xff0c;若開啟混合持久化方式則會創(chuàng)建一個文件&#xff0c;上面是rdb&#xff0c;下面是aof的數(shù)據(jù)&#xff0c;啟動加載這個文件

7.4、Redis持久化數(shù)據(jù)和緩存怎么做擴(kuò)容&#xff1f;

1、如果Redis被當(dāng)做緩存使用&#xff0c;使用一致性哈希實(shí)現(xiàn)動態(tài)擴(kuò)容縮容。2、如果Redis被當(dāng)做一個持久化存儲使用&#xff0c;必須使用固定的keys-to-nodes映射關(guān)系&#xff0c;節(jié)點(diǎn)的數(shù)量一旦確定不能變化。否則的話(即Redis節(jié)點(diǎn)需要動態(tài)變化的情況&#xff09;&#xff0c;必須使用可以在運(yùn)行時(shí)進(jìn)行數(shù)據(jù)再平衡的一套系統(tǒng)&#xff0c;而當(dāng)前只有Redis集群可以做到這樣

7.5、內(nèi)存淘汰策略

a) 針對設(shè)置了過期時(shí)間的key做處理&#xff1a;	1、volatile-ttl&#xff1a;在篩選時(shí)&#xff0c;會針對設(shè)置了過期時(shí)間的鍵值對&#xff0c;根據(jù)過期時(shí)間的先后進(jìn)行刪除&#xff0c;越早過期的越先被刪除。	2、volatile-random&#xff1a;就像它的名稱一樣&#xff0c;在設(shè)置了過期時(shí)間的鍵值對中&#xff0c;進(jìn)行隨機(jī)刪除。	3、volatile-lru&#xff1a;會使用 LRU 算法篩選設(shè)置了過期時(shí)間的鍵值對刪除。	4、volatile-lfu&#xff1a;會使用 LFU 算法篩選設(shè)置了過期時(shí)間的鍵值對刪除。
b) 針對所有的key做處理&#xff1a;	5、allkeys-random&#xff1a;從所有鍵值對中隨機(jī)選擇并刪除數(shù)據(jù)。	6、allkeys-lru&#xff1a;使用 LRU 算法在所有數(shù)據(jù)中進(jìn)行篩選刪除。	7、allkeys-lfu&#xff1a;使用 LFU 算法在所有數(shù)據(jù)中進(jìn)行篩選刪除。
c) 不處理&#xff1a;8、noeviction&#xff1a;不會剔除任何數(shù)據(jù)&#xff0c;拒絕所有寫入操作并返回客戶端錯誤信息"(error)OOM command not allowed when used memory"&#xff0c;此時(shí)Redis只響應(yīng)讀操作。

7.6、數(shù)據(jù)刪除策略

1、被動刪除&#xff1a;當(dāng)讀/寫一個已經(jīng)過期的key時(shí)&#xff0c;會觸發(fā)惰性刪除策略&#xff0c;直接刪除掉這個過期key2、主動刪除&#xff1a;由于惰性刪除策略無法保證冷數(shù)據(jù)被及時(shí)刪掉&#xff0c;所以Redis會定期主動淘汰一批已過期的key3、當(dāng)前已用內(nèi)存超過maxmemory限定時(shí)&#xff0c;觸發(fā)主動清理策略4、LRU 算法&#xff08;Least Recently Used&#xff0c;最近最少使用&#xff09;&#xff1a;淘汰很久沒被訪問過的數(shù)據(jù)&#xff0c;以最近一次訪問時(shí)間作為參考。5、LFU 算法&#xff08;Least Frequently Used&#xff0c;最不經(jīng)常使用&#xff09;&#xff1a;淘汰最近一段時(shí)間被訪問次數(shù)最少的數(shù)據(jù)&#xff0c;以次數(shù)作為參考

7.7、緩存擊穿/緩存雪崩/緩存穿透/熱點(diǎn)緩存key重建優(yōu)化/緩存與數(shù)據(jù)庫雙寫不一致

7.7.1、緩存擊穿&#xff08;失效&#xff09;

7.7.1.1、原因
由于大批量緩存在同一時(shí)間失效可能導(dǎo)致大量請求同時(shí)穿透緩存直達(dá)數(shù)據(jù)庫&#xff0c;可能會造成數(shù)據(jù)庫瞬間壓力過大甚至掛掉
7.7.1.2、解決方案
1、在批量增加緩存時(shí)將這一批數(shù)據(jù)的緩存過期時(shí)間設(shè)置為一個時(shí)間段內(nèi)的不同時(shí)間。2、分布式鎖

7.7.2、緩存雪崩

7.7.2.1、原因
緩存雪崩指的是緩存層支撐不住或宕掉后&#xff0c; 流量會像奔逃的野牛一樣&#xff0c; 打向后端存儲層。由于緩存層承載著大量請求&#xff0c; 有效地保護(hù)了存儲層&#xff0c; 但是如果緩存層由于某些原因不能提供服務(wù)(比如超大并發(fā)過來&#xff0c;緩存層支撐不住&#xff0c;或者由于緩存設(shè)計(jì)不好&#xff0c;類似大量請求訪問bigkey&#xff0c;導(dǎo)致緩存能支撐的并發(fā)急劇下降)&#xff0c; 于是大量請求都會打到存儲層&#xff0c; 存儲層的調(diào)用量會暴增&#xff0c; 造成存儲層也會級聯(lián)宕機(jī)的情況。
7.7.2.2、解決方案
1&#xff09; 保證緩存層服務(wù)高可用性&#xff0c;比如使用Redis Sentinel或Redis Cluster。2&#xff09; 依賴隔離組件為后端限流熔斷并降級。比如使用Sentinel或Hystrix限流降級組件。
比如服務(wù)降級&#xff0c;我們可以針對不同的數(shù)據(jù)采取不同的處理方式。當(dāng)業(yè)務(wù)應(yīng)用訪問的是非核心數(shù)據(jù)&#xff08;例如電商商品屬性&#xff0c;用戶信息等&#xff09;時(shí)&#xff0c;暫時(shí)停止從緩存中查詢這些數(shù)據(jù)&#xff0c;而是直接返回預(yù)定義的默認(rèn)降級信息、空值或是錯誤提示信息&#xff1b;當(dāng)業(yè)務(wù)應(yīng)用訪問的是核心數(shù)據(jù)&#xff08;例如電商商品庫存&#xff09;時(shí)&#xff0c;仍然允許查詢緩存&#xff0c;如果緩存缺失&#xff0c;也可以繼續(xù)通過數(shù)據(jù)庫讀取。3&#xff09; 提前演練。 在項(xiàng)目上線前&#xff0c; 演練緩存層宕掉后&#xff0c; 應(yīng)用以及后端的負(fù)載情況以及可能出現(xiàn)的問題&#xff0c; 在此基礎(chǔ)上做一些預(yù)案設(shè)定。

7.7.3、緩存穿透

7.7.3.1、原因
緩存穿透是指查詢一個根本不存在的數(shù)據(jù)&#xff0c; 緩存層和存儲層都不會命中&#xff0c; 通常出于容錯的考慮&#xff0c; 如果從存儲層查不到數(shù)據(jù)則不寫入緩存層。
緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請求都要到存儲層去查詢&#xff0c; 失去了緩存保護(hù)后端存儲的意義。
造成緩存穿透的基本原因有兩個&#xff1a;
	第一&#xff0c; 自身業(yè)務(wù)代碼或者數(shù)據(jù)出現(xiàn)問題。
	第二&#xff0c; 一些惡意攻擊、 爬蟲等造成大量空命中。
7.7.3.2、解決方案
1、緩存空對象2、布隆過濾器&#xff08;redission里面有個getBloomFilter()方法實(shí)現(xiàn)&#xff0c;==布隆過濾器不能刪除數(shù)據(jù)&#xff0c;如果要刪除得重新初始化數(shù)據(jù)==&#xff09;

7.7.4、熱點(diǎn)緩存key重建優(yōu)化

7.7.4.1、原因
開發(fā)人員使用“緩存+過期時(shí)間”的策略既可以加速數(shù)據(jù)讀寫&#xff0c; 又保證數(shù)據(jù)的定期更新&#xff0c; 這種模式基本能夠滿足絕大部分需求。 但是有兩個問題如果同時(shí)出現(xiàn)&#xff0c; 可能就會對應(yīng)用造成致命的危害&#xff1a;
	當(dāng)前key是一個熱點(diǎn)key&#xff08;例如一個熱門的娛樂新聞&#xff09;&#xff0c;并發(fā)量非常大。
	重建緩存不能在短時(shí)間完成&#xff0c; 可能是一個復(fù)雜計(jì)算&#xff0c; 例如復(fù)雜的SQL、 多次IO、 多個依賴等。
在緩存失效的瞬間&#xff0c; 有大量線程來重建緩存&#xff0c; 造成后端負(fù)載加大&#xff0c; 甚至可能會讓應(yīng)用崩潰。
要解決這個問題主要就是要避免大量線程同時(shí)重建緩存。
7.7.4.2、解決方案
互斥鎖&#xff08;也就是所謂的分布式鎖&#xff09;

7.7.5、緩存與數(shù)據(jù)庫雙寫不一致

1、可以通過加讀寫鎖保證并發(fā)讀寫或?qū)憣懙臅r(shí)候按順序排好隊(duì)&#xff0c;讀讀的時(shí)候相當(dāng)于無鎖。2、可以用阿里開源的canal通過監(jiān)聽數(shù)據(jù)庫的binlog日志及時(shí)的去修改緩存&#xff0c;但是引入了新的中間件&#xff0c;增加了系統(tǒng)的復(fù)雜度。3、先刪緩存&#xff0c;再寫數(shù)據(jù)庫
&#xff08;1&#xff09;timer異步淘汰&#xff08;本文沒有細(xì)講&#xff0c;本質(zhì)就是起個線程專門異步二次淘汰緩存&#xff09;
&#xff08;2&#xff09;總線異步淘汰
&#xff08;3&#xff09;讀binlog異步淘汰//blog.csdn.net/zhouhengzhe?t=1

到此,關(guān)于“JVM+Redis+SpringBoot的面試題有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI