哈嘍哈嘍大家猴,我是把代碼寫成bug的大頭菜。公眾號:大頭菜技術(bigheadit)。原創(chuàng)不易,但歡迎轉(zhuǎn)載。
最近這個星期。主要兩件事兒:
- 工作遇到的bug和總結(jié)
- 重新梳理JVM的基礎知識
工作遇到的bug和總結(jié)
最近這兩周,因為做了4個需求,都是關于黑白名單的。
于是我就打算,把這些黑白名單的需求,比如有關于C端用戶的白名單,C端用戶的黑名單,B端用戶的白名單,B端用戶的黑名單,我打算直接抽象一點,把4個需求抽象整合為一個需求。每個黑白名單需求:都有增加黑白名單,刪除黑白名單,查詢黑白名單三種不同的接口。
因為之前,已經(jīng)有C端用戶的白名單了。但是對應的表的設計沒有考慮好拓展性,沒法兼容C端用戶的黑名單等需求。因此需要重新設計表。
新表的設計關鍵字段:
- role:角色
- 1:C端用戶
- 2:B端用戶
- type:黑白名單
- 1:白名單
- 2:黑名單
- status:數(shù)據(jù)狀態(tài)
- 1:生效
- 2:失效
- businessCode:業(yè)務線
- 1:電商業(yè)務線
- 2:外賣業(yè)務線
- 3:物流業(yè)務線
重新設計后,表的可拓展性大大提高。但業(yè)務的復雜度和開發(fā)復雜度也大大提高。魚和熊掌不可兼得嘛。正常!
其實,把4個需求,抽象合并為一個需求,是降低了開發(fā)成本的,也對業(yè)務做了比較好的抽象,可以為以后其他類似的需求,比如:新增一條業(yè)務線D的白名單。
這樣子,其實就是,在枚舉上增加多一個枚舉。在接口入?yún)⑸?,改變一下參?shù)值即可。核心代碼,幾乎不用變。
如果考慮某些需要定制化的黑白名單。我們采用策略工廠模式即可。這樣子,代碼的可讀性大大提高,同時也滿足開閉原則。
bug的潛在
因為涉及到表的重新設計,因此舊表的數(shù)據(jù),也需要做同步遷移。數(shù)據(jù)遷移,這種寫一下SQL語句就好。沒什么特別的。
關鍵點:我忘了誰在使用舊表。
就是哪些接口調(diào)用了獲取舊表的數(shù)據(jù)。當時,的確忘了這茬,也沒做兼容。因此,bug就來了。
這次,也真算是,把代碼寫成bug了哈哈哈哈。
影響的范圍:調(diào)用了訪問舊表數(shù)據(jù)的接口。
對于這次bug的影響,我做了一些總結(jié)點
- 涉及到表數(shù)據(jù)遷移時,需要考慮兼容性,包括讀和寫,只要漏掉其中一個,bug肯定就會有
- 如果真的實現(xiàn),沒法兼容原來的接口,這個時候,最好在大群里,廣播通知一下各個調(diào)用方,好讓他們盡快切換接口。
- 在分布式項目中,其實有一個痛點:就是接口不知道被哪個微服務調(diào)用了。比如:服務A的A接口,被服務B的B接口調(diào)用了。但是因為這是2個不同微服務,沒法找對應的調(diào)用鏈。我目前也沒找到好的辦法來解決微服務的調(diào)用鏈問題。
重新梳理JVM的基礎知識
最近重新梳理了一下JVM的基礎知識。相關筆記如下:
Q:我們平時寫的JAVA代碼是怎么運行起來的
-
A:
- 1、把.java代碼編譯為.class代碼
- 2、類加載器把.class代碼加載到jvm中,是字節(jié)碼執(zhí)行引擎按需加載的,不是一次性加載全部.class代碼
- 3、字節(jié)碼執(zhí)行引擎執(zhí)行.class代碼
Q:既然.java代碼能編譯成.class代碼后運行,那么肯定也可以把.class代碼反編譯為.java代碼。如果這樣子的話,黑客拿到.class代碼后,就可以通過反編譯拿到核心.java代碼。對于這個問題,應該怎么解決。
-
A:
- 1、代碼混淆工具
- 2、可以自定義類加載器。然后用公鑰和私鑰做加密。
- Q:JVM是什么?
- A:JVM本質(zhì)上也是一個程序,負責運行java編譯好的class代碼
Q:JVM跟我們平時運行的機器上的系統(tǒng)有什么區(qū)別?
A:JVM具有跨平臺。系統(tǒng)不具備跨平臺。
Q:類加載器的概念
A:把編譯好的class文件按需加載到JVMzhong
Q:什么是字節(jié)碼執(zhí)行引擎
A:針對加載進內(nèi)存的class代碼進行運行
Q:類加載的流程
-
A:
- 1、JVM一般會在使用到具體某個類時,才會去加載對應的類。按需加載
- 2、加載到JVM后,需要對class代碼進行語法驗證操作
- 3、驗證通過后,需要對類分配空間,對類變量分配空間,并且賦予默認值。
- 4、解析,就是符號引用替換為直接引用,說了等于沒說。這個階段,對我們java開發(fā)沒多大用處。跳過吧
Q:什么時候會初始化一個類?
A:new 類()的實例化的對象的時候,就會觸發(fā)從加載到初始化的全過程。如果是包含main方法的類,必須啟動時,就立馬對類進行初始化。如果這個類還有父類,且父類還沒被加載和初始化,這時候,需要先加載和初始化其父類,完成父類的初始化后,再回去初始化自己。
Q:為什么不把lib/ext的jar一起放在啟動類加載器中一起加載?
-
A:
- 1、為了區(qū)分同名類
- 2、允許你再一個jvm里運行不同的應用程序
- 3、對不同的類庫進行加強。
- Q:線程、java虛擬機棧、棧幀、局部變量、方法的關系
- A:
- 一個線程,有一個java虛擬機棧。
- 一個線程,有一個程序計數(shù)器
- 一個java虛擬機棧,有多個棧幀
- 一個線程,每訪問一個方法,就創(chuàng)建一個棧幀。
- 一個棧幀,有多個局部變量。
Q:spring boot中怎么設置JVM參數(shù)?
A:啟動的時候設置
Q:tomcat 中怎么設置JVM參數(shù)?
A:catalina.sh 文件配置參數(shù)
Q:系統(tǒng)并發(fā)量大時,系統(tǒng)會變慢,進而導致什么?
A:系統(tǒng)并發(fā)量大的時候,會有一些請求特別慢,進而引用著新生代的對象,但是GC時無法回收,因為還被引用著。多次minor gc后,特別慢的請求對應的對象,會進入老年代。進而很快把老年代填滿,進而導致頻繁的Full GC。
- Q:什么情況JVM內(nèi)存中的一個對象會被回收?
- A:GC Root。比如局部變量和靜態(tài)變量。此外,還分強引用,軟引用。虛引用。
- 強引用,只要被GC Root引用,就一定不會被回收。
- 軟引用,是在垃圾回收后,仍然不夠內(nèi)存空間,才會被回收,不管是否被引用。
- 虛引用,垃圾回收,就會被回收,不管是否被引用。
- Q:對象在新生代的分配?
- A:優(yōu)先在eden區(qū)分配
- Q:什么時候會觸發(fā)Minor GC
- A:eden區(qū)滿了。
- Q:觸發(fā)minor gc的情況有:
- A:
- 1、新生代現(xiàn)有對象小于老年代剩余內(nèi)存,即老年代空間足以支撐可能晉升的對象
- 2、情況1不成立,查看設置了空間擔保且擔保成功
什么情況下Minor GC之前會提前觸發(fā)Full GC?
-
A:
- 1、新生代的全部對象大小如果大于老年代的剩余空間
- 2、沒有空間擔保機制
- 3、擔保失敗
Q:什么情況下會直接觸發(fā)Minor GC
A:eden區(qū)滿了
Q:Minor GC之后有哪幾種情況對象會進入老年代
-
A:
- 1、survivor區(qū)不能容納eden區(qū)的存活對象
- 2、動態(tài)年齡判斷:survivor區(qū)的同一年齡對象超過survivior空間的一半
Q:優(yōu)化系統(tǒng)性能的思路?
A 核心思路:盡可能讓對象不要過快進入老年代。減少老年代的full gc。盡可能讓對象在新生代就被GC回收。
Q:如何讓對象不要過快進入老年代?
A:survivor要有足夠的空間。試想一下,如果survivor區(qū),空間不夠,對象就會直接進入老年代。即使夠,但是超過survivor區(qū)的一半,也會因為動態(tài)年齡判斷,而直接進入老年代。