1. 程式人生 > 其它 >學習記錄-《碼出高效》

學習記錄-《碼出高效》

2020/08/31 1、邏輯或、邏輯與運算只能針對布林型別的條件表示式進行運算。 2、計算機定義了兩種小數:定點數和浮點數 3、IEEE754浮點數標準,規定了4種浮點數型別:單精度、雙精度、延伸單精度、延伸雙精度 4、浮點數使用:   在使用浮點數的時候推薦使用雙精度,使用單精度由於表示區間的限制,計算結果會出現微小的誤差。 在要求絕對精確表示的業務場景下,比如金融行業的貨幣表示,推薦使用整型儲存器最小單位的值,展示時可以轉換成該貨幣的常用單位,比如人命幣使用分儲存,美元使用美分儲存。在要求精確表示小數點n位的業務場景下,比如圓周率要求儲存小數點後1000位數字,使用單精度和雙精度浮點數型別儲存都是難以做到的,這時推薦使用陣列儲存小數部分的資料。 在比較浮點數時,會存在誤差。禁止通過判斷兩個浮點數是否相等來控制業務流程。在資料庫中儲存小數時,推薦使用decimal型別,禁止使用float和double型別,因為這兩種型別在儲存的時候會有精度損失。 綜上所述,在要求絕對精度表示的業務場景中,在小數儲存、計算、轉型過程中要謹慎對待。 5、cpu由控制器和運算器組成,內部暫存器使這兩者協同更加高效。 2020/09/01 TCP三次握手建立連線(二次會導致髒連線) TCP四次揮手斷開連線 從經驗來看,資料庫層面的請求應答時間必須在100ms內,秒級sql查詢通常存在巨大的效能提升空間。有如下應對方案: 1)建立高效且合適的索引 2)排查連線資源未顯式關閉的情形 3)合併短請求 4)合理拆分多個表join的SQL,若是超過三個表則禁止join 5)使用臨時表 6)應用層優化 7)改用其他資料庫 SQL注入解決辦法: 1)過濾使用者輸入引數中的特殊字元 2)禁止通過字串拼接的SQL語句,嚴格使用引數繫結傳入的sql引數 3)合理使用資料庫訪問框架提供的防止注入機制,mybatis禁止使用${} 防範XSS攻擊:前端使用innerText 而不是innerHtml 防範CSRF漏洞主要通過以下方式: 1)CSRF token驗證 2)人機互動,比如簡訊驗證 2020/09/02: 1、安全套接字層:Secure socket layer - SSL 2、HTTPS ->HTTP OVER SSL 3、對稱加密演算法:DES 4、非對稱加密演算法:RSA 5、優秀的程式設計師至少需要掌握3門語言,這有助於知曉不同語言的各自特性,更重要的是洞悉語言共性和程式語言思想,跨域語言的抽象思維和架構掌控力。但是掌握不等於精通,真正的大師,需要醉心於某種語言,不斷研究、不斷打磨、不斷回爐,才能達到爐火純青、登峰造極的境界。我們寫的每一行程式碼都是站在巨人的肩膀上,使我們看得更遠。雖然任何程式語言的結構都是順序、條件、迴圈,任何程式語言的本質都是輸入輸出,但是0與1的世界一定會因為程式設計變得更智慧,更美好。
6、慎用object的clone()方法,因為物件的clone()方法是淺拷貝,若想實現深拷貝,則需要覆寫clone()方法實現引用物件的深度遍歷式拷貝。 7、設計模式七大原則之一的迪米特法則就是對於封裝的具體要求,即A模組使用B模組的某個介面行為,對B模組中除此之外的其他資訊知道得儘可能少。 8、繼承的is-a判斷標準:是否符合里氏代換原則(LSP)任何父類能出現的地方子類都能夠出現。 9、提倡組合優先的原則來擴充套件類的能力,即優先採用組合或者聚合的類關係來複用其他類的能力,而不是繼承。 10、JDK5~JDK8的重要特性: JDK5新特性:foreach迭代方式,可變引數,列舉,自動拆裝箱,泛型,註解等重要特性(歷史意義的版本) JDK6新特性:。。。 JDK7新特性:switch支援字串作為匹配條件、泛型型別自動推斷、try-with-resource資源關閉技巧,Objects工具類,ForkJoinPool等重要類與特性。 JDK8新特性:lambda表示式、函數語言程式設計、引入Optional避免空指標 11、抽象類是模版式設計,介面是契約式設計。
12、定義包內可見靜態內部類的方式很常見,這樣做的好處是: 1)作用域不會擴散到包外 2)可以通過“外部類.內部類”的方式直接訪問 3)內部類可以訪問外部類中所有的靜態屬性和方法 13、在定義類時,推薦訪問控制級別從嚴處理: 1)如果不允許外部直接通過new建立物件,構造方法必須是private 2)工具類不允許有public和default構造方法 3)類非static成員變數並且與子類共享,必須是protected 4)類非static成員變數並且僅在本類使用,必須是private 5)類static成員變數,如果僅在本類使用,必須是private 6)若是static成員變數必須考慮是否為final 7)類成員方法只供類內部呼叫,必須是private 8)類成員方法只對繼承類公開,那麼限制為protected 14、訪問許可權控制符:無 不為default 2020/09/21 1、儘量不使用可變引數。如果一定要使用,則只有相同引數型別,相同業務含義的引數才可以。並且一個方法只能有一個可變引數,且為該方法的最後一個引數。不推薦使用Object作為可變引數型別。 2、正確使用引數,引數預處理,包含兩種情況: 1)入參保護。常見於批量介面。如:資料量過多導致記憶體佔滿。 2)引數校驗 3、構造方法: 1)構造方法名稱必須與類名相同。 2)構造方法沒有返回型別,void也不能有。它返回物件地址,並賦值給引用變數。 3)不能繼承,不能覆寫,不能被直接呼叫。呼叫途徑有三種:new、super、通過反射方式獲取並呼叫。 4)定義類時提供了無參的構造方法。如果定義了有引數構造方法,還需要原始構造方法,則需要顯式定義。 5)構造方法可以私有 注意:構造方法應遵循單一職責原則,不應在構造方法中引入業務邏輯。 4、類物件建立: 先執行父類和子類的靜態程式碼塊,然後再執行父類和子類的構造方法,靜態程式碼塊只執行一次,在第二次物件例項化時不會執行。 2020/10/17: 1、類內方法 除構造方法外類中還可以有三類方法:例項方法、靜態方法、靜態程式碼塊 2、靜態方法: 1)靜態方法中不能使用例項成員變數和例項方法 2)靜態方法不能使用this和super關鍵字,這兩個關鍵字指代的都是需要被創建出來的物件 3、靜態程式碼塊 1)靜態程式碼塊是先於構造方法執行的特殊程式碼塊 2)不能存在於任何方法體內,包括類靜態方法和屬性變數 4、getter和setter 一般不包含任何業務邏輯 好處: 1)滿足面嚮物件語言封裝的特性 2)有利於統一控制。如對物件屬性修改作許可權控制可在setter處做處理 易出錯點: 1)在setter/getter中新增業務邏輯很難排查 2)同時定義iSXxxx()和getXxxx(),兩者同時存在會在iBATIS和Json序列化引起衝突 3)相同的屬性名容易帶來歧義 非getter/setter引數名不能語成員變數相同 5、覆寫-動態繫結 如果父類的方法不能滿足子類的期望,那麼子類可以重新實現方法覆蓋父類的實現,因為有些子類是延遲載入,甚至是網路載入的,所以最終的是現實需要在執行期判斷,這就是-動態繫結 通過父類執行子類方法時需要注意以下兩點: 1)無法呼叫到子類中存在而父類本身不存在的方法 2)可以呼叫到子類中覆寫了父類的方法,這是一種多型實現 想要成功覆寫父類方法,需要滿足4個條件(轉型滿足LSP李氏替換原則): 1)訪問許可權不能變小 2)返回型別能夠向上轉型為父類的返回型別 3)異常也要能向上轉型為父類的異常 4)方法名和引數列表必須完全一致 總結為“一大兩小兩同” 6、過載-靜態繫結 1)在編譯器眼裡,方法名+引數列表組成一個唯一鍵,稱為方法簽名 2)JVM在過載方法中選擇合適的目標方法的順序如下: a、 精確匹配 b、如果是基本資料型別,自動轉換為更大表示範圍的基本型別 c、通過自動拆箱與裝箱 d、通過子類向上轉型繼承路線依次匹配 e、通過可變引數匹配(優先順序最低) 2020/10/15: 1、Java 9種基本資料型別: boolean,byte,char,short,int,long,float,double,refvar(引用變數,也叫引用控制代碼) 2、不能用雙引號的方式對char型別進行賦值 3、引用分為兩種資料型別:引用變數本身,和引用指向的物件 4、基本資料型別int佔用4個位元組,而對應的包裝類Integer例項物件佔用16個位元組 5、類定義中的方法程式碼不佔用例項物件的任何空間 1)物件頭:物件頭最小佔用12位元組空間(包括物件標記8位元組和類元資訊)4位元組 2)例項資料:例項成員變數和所有可見的父類成員變數 3)對齊填充:物件的儲存空間分配單位是8個位元組,如果需要佔用的記憶體不為8的倍數,則補充為相近最小8的倍數 6、各個包裝類的快取區間: Boolean:使用靜態final變數定義,valueOf就是返回這兩個靜態值 Byte:表示範圍是-128~127,全部快取 Short:表示範圍是-32767~32767,快取範圍是-128~127 Character:表示範圍是0~65535,快取範圍是0~127 Long:表示範圍是-2的63次方~2的63次方-1,快取範圍是-128~127 Integer:表示範圍是-2的31次方~2的31次方-1,快取範圍是-128~127。在VM options加入引數-XX:AutoBoxCacheMax=7777即可設定最大快取值為7777 7、在選擇使用包裝類還是基本資料型別時,推薦使用如下方式: 1)所有的POJO類屬性必須使用包裝型別 2)RPC方法的返回值和引數必須使用包裝資料型別 3)所有的區域性變數推薦使用基本資料型別 8、命名規約 1)命名符合本語言特性 2)命名體現程式碼元素特徵 3)最好望文知意 9、常量:常量是在作用域內保持不變的值,一般使用final關鍵字進行修飾。根據作用域區分為3類: 1)全域性常量:指的是類的公開靜態屬性,使用public static final 修飾 2)類內常量:是私有靜態屬性,使用private static final 修飾 3)區域性常量:分為方法常量和引數常量,前者是在方法或者程式碼塊內部定義的常量,後者是在定義形式引數時,增加final標誌,表示此引數值不能被修改 10、一般型別用列舉表示,狀態可用列舉也可用不能被例項化的抽象類的全域性常量來表示,從而避免魔法值。 11、控制語句: 1)在if,else,for,while,do-while等語句中必須使用大括號 2)在條件表示式中不能有賦值操作,也不要在判斷表示式中出現複雜的邏輯組合 3)多層巢狀不能超過3層 4)避免採用反邏輯運算子 JVM
1、類載入過程: 1)Load-載入:讀取類檔案產生的二進位制流,並轉化為特定的資料結構,初步校驗cafe babe魔法數、常量池、檔案長度、是否有父類等,然後建立對應的java.lang.Class例項 2)Link-連結:包括驗證、準備、解析三個步驟。驗證是更加詳細的校驗,比如final是否合規、型別是否正確、靜態變數是否合理等;準備階段是為靜態變數分配記憶體,並設定預設值,解析類和方法確保類與類之間的額相互引用正確性,完成記憶體結構佈局 3)Init-初始化:執行類構造器<clinit>方法,如果賦值運算是通過其他類的靜態方法來完成的,那麼馬上會解析另外一個類,在虛擬機器棧中執行完畢後通過返回值進行賦值。 2、在什麼情況下需要自定義類載入器? 1)隔離載入類 2)修改類載入方式 3)擴充套件載入源 4)防止程式碼洩露 3、記憶體佈局:堆、元空間、虛擬機器棧、本地方法棧、程式計數器 1)堆是OOM的主要產生地,分為兩大塊:新生代和老年代 2)棧幀在整個JVM體系中地位頗高,包括區域性變量表、操作棧、動態連線、方法返回地址等 總結:從執行緒共享的角度來看,堆和元空間是所有執行緒共享的,而虛擬機器棧、本地方法棧、程式計數器是執行緒內部私有的。 2020/11/09: 併發與多執行緒: 1、執行緒狀態:NEW (新建狀態)、RUNNABLE(就緒狀態)、 RUNNING (執行狀態)、BLOCKED(阻塞狀態) DEAD(終止狀態)五種狀態 (1)、NEW 新建狀態,是執行緒被建立且未啟動的狀態。建立執行緒的方式有3種: 第一種是繼承自Thread類,第二種是實現Runnable介面,第三種是實現Callable介面。相比第一種推薦第二種方式,因為繼承自Thread類往往不符合里氏代換原則。而實現Runnable介面可以使程式設計更加靈活,對外暴露的細節比較少,讓使用者專注於實現執行緒的run()方法上。 Callable與Runnable有兩點不同:第一可以通過call()返回值,前兩種方式都不能在任務執行完成後,直接獲取執行結果,需要藉助共享變數等來獲取,而Callable和Future則很好地解決了這個問題,第二,call()可以丟擲異常,而Runnable只有通過setDefaultUncaughtExceptionHandler()的方式才能在主執行緒中捕捉到子執行緒異常。 (2)、Runnable,就緒狀態,是呼叫start()之後執行之前的狀態。 (3)、RUNNING,執行狀態,是run()正在執行時執行緒的狀態。 (4)、BLOCKED,進入此狀態,有以下三種情況: 同步阻塞:鎖被其他執行緒佔用 主動阻塞:呼叫THread的某些方法,主動讓出CPU執行權,比如sleep(), join()等 等待阻塞:執行了wait() (5)DEAD,終止狀態,是run()執行結果,或因異常退出後的狀態,此狀態不可逆轉 2、為保證執行緒安全,在多個執行緒併發地競爭共享資源時,通常採用同步機制協調各個執行緒的執行,以確保得到正確的結果。 3、執行緒安全問題只在多執行緒環境下才出現,單執行緒序列執行不存在此問題。保證高併發場景下的執行緒安全,可以從以下四個維度考量。 (1)、資料單執行緒內可見 (2)、只讀物件 (3)、執行緒安全類 (4)、同步與鎖機制 4、執行緒安全的核心理念就是“要麼只讀,要麼加鎖” 5、Java併發包(Java.util.concurrent,JUC)主要分成以下幾個類族: (1)執行緒同步類 (2)併發集合類 (3)執行緒管理類 (4)鎖相關類 6、計算機鎖也是從開始的悲觀鎖,發展到後來的樂觀鎖、偏向鎖、分段鎖等。鎖主要提供了兩種特性:互斥性和不可見性 7、Java中實現鎖的方式有兩種 (1)用併發包中的鎖類 Lock是JUC包的頂層介面,它的實現邏輯並未使用synchronized而是利用了volatile的可見性 (2)是利用同步程式碼塊 同步程式碼塊一般使用Java的synchronized關鍵字來實現,有兩種方式對方法進行加鎖操作: 第一在方法簽名處加synchronized關鍵字 第二使用synchronized(物件或者類)進行同步。 這裡的原則是鎖的範圍儘可能小,鎖的時間儘可能短,即能鎖物件就不要鎖類,能 鎖程式碼塊就不要鎖方法。 8、JDK6以後不斷優化使得synchronized提供三種鎖的實現,包括偏向鎖、輕量級鎖、重量級鎖,還提供了自動的降級和升級機制。JVM就是利用CAS在物件頭上設定執行緒ID,表示這個物件偏向於當前執行緒,這就是偏向鎖。 9、偏向鎖是為了在資源沒有被多執行緒競爭的情況下儘量減少鎖帶來的效能開銷。在鎖物件的物件頭重有一個ThreadId欄位,當第一個執行緒訪問鎖時,如果該鎖沒有被其他執行緒訪問過,即ThreadId欄位為空,那麼JVM讓其持有偏向鎖,並將ThreadId欄位的值設定為該執行緒的ID,當下一次獲取鎖時,會判斷它們是否一致,如果一致,那麼該執行緒不會重新獲取鎖,從而提高了程式的執行效率。如果出現鎖的競爭情況,那麼偏向鎖會被撤銷,並被升級為輕量級鎖,如果競爭非常激烈會升級為重量級鎖。偏向鎖可以減低無競爭開銷,它不是互斥鎖,不存線上程競爭的情況,省去再次同步判斷的步驟,提升了效能。 10、資源共享的兩個原因是資源緊缺和共建需求。執行緒共享cpu是從資源緊缺的維度來考慮的,而多執行緒共享同一變數,通常是從共建需求的維度來考慮的。 11、原子性:指不可分割的一系列操作指令,在執行完畢前不會被其他任何操作中斷,要麼全部執行,要麼全部不執行。 2020/11/10: 執行緒池: 1、執行緒池的好處 執行緒使應用能夠更加充分合理地協調利用CPU、記憶體、網路、I/O等系統資源。 執行緒池的作用包括: (1)利用執行緒池管理並複用、控制最大併發數等。 (2)實現任務執行緒佇列快取策略和拒絕機制。 (3)實現某些與時間相關的功能,如定時執行、週期執行等 (4)隔離執行緒環境 2020/11/11: ThreadPoolExecutor的構造方法如下:
public ThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler) {
        // maximumPoolSize必須大於或等於1也要大於或等於corePoolSize(第一處)
        if(corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0){
            throw new IllegalArgumentException();}
        // 第二處
        if(workQueue == null || threadFactory == null || handler == null){
            throw new NullPointerException();
        }
        // 其他程式碼 ...
    }

第一個引數:corePoolSize表示常駐核心執行緒數。如果等於0,則任務執行完之後,沒有任何請求進入時會銷燬執行緒池的執行緒;如果大於0,即使本地任務執行完畢,核心執行緒也不會被銷燬。這個值的設定非常關鍵,設定過大會浪費資源,設定過小會導致執行緒頻繁地建立或銷燬。

第二個引數:maximumPoolSize表示執行緒池能容納同時執行的最大執行緒數。從上方示例程式碼中的第一處看來,必須大於或者等於1。如果maximumPoolSize與corePoolSize相等,即是固定大小執行緒池。 第三個引數:keepAliveTime表示執行緒池中的執行緒空閒時間,當空閒時間達到keepAliveTime值時,執行緒會被銷燬,直到只剩下corePoolSize個執行緒為止,避免浪費記憶體和控制代碼資源。在預設情況下,當執行緒池的執行緒數大於corePoolSize時,keepAliveTime才會起作用。但是當ThreadPoolExecutor的allowCoreThreadTImeOut變數設定為true時,核心執行緒超時後也會被回收。 第四個引數:TimeUnit 表示時間單位。keepAliveTime的時間單位通常是TimeUnit.SECONDS。 第五個引數:workQueue表示快取佇列。當請求的執行緒數大於corePoolSize時,執行緒進入BlockingQueue阻塞佇列,BlockingQueue佇列快取達到上限後,如果還有新任務需要處理,那麼執行緒池會建立新的執行緒,最大執行緒數為maximumPoolSize。 第六個引數:threadFactory 表示執行緒工廠。它用來生產一組相同任務的執行緒。執行緒池的命名是通過給這個factory增加組名字首來實現的。在虛擬機器棧分析時,就可以知道執行緒任務是由哪個執行緒工廠生產的。 第七個引數:handler表示執行拒絕策略的物件。當第五個引數workQueue的任務快取區達到上限後,並且活動執行緒數大於maximumPoolSize的時候,執行緒池通過該策略處理請求,這是一種簡單的限流保護。 友好的拒絕策略可以是如下三種: (1)儲存到資料庫進行削峰填谷。在空閒時再提取出來執行。 (2)轉向某個提示頁面 (3)列印日誌 2020/11/11: 1、Executor介面有且只有一個方法execute,通過引數傳入待執行執行緒的物件 2、使用執行緒池要注意如下幾點: (1)合理設定各種引數,根據實際的業務場景來設定合理的工作執行緒數 (2)執行緒資源必須通過執行緒池提供,不允許在應用中自行顯式建立執行緒 (3)建立執行緒或執行緒池時請指定有意義的執行緒名稱,方便出錯時的回溯 執行緒池不允許使用Executors,而是通過ThreadPoolExecutor的方式建立,這樣的處理方式更加明確執行緒池的執行規則,規避資源耗盡的風險。 3、引用型別,物件在堆上建立之後所持有的引用其實是一種變數型別,引用之間可以通過賦值構成一條引用鏈 4、引用可分為:強引用,軟引用,弱引用,虛引用 強引用:Strong Reference,最為常見。如new出來的物件。只要物件有強引用指向,並且GC Roots 可達,那麼Java記憶體回收時,即使記憶體耗盡也不會回收該物件。 軟引用:Soft reference,引用力度弱於“強引用”,是用在非必須物件的場景。在即將OOM之前,垃圾回收器會把這些軟引用指向的物件加入回收範圍,以獲得更多的記憶體空間。主要用來快取伺服器中間計算結果及不需要儲存的使用者行為等。 弱引用: Weak Reference,引用較前兩者更弱,用來描述非必須物件。如果弱引用指向的物件只存在弱引用這一條線路,則在下一次YGC時會被回收。由於YGC時間的不確定性,弱引用被回收也具有不確定性。弱引用主要用於指向某個易消失的物件,在強引用斷開後,此引用不會劫持物件。呼叫WeakReference.get()可能返回null,注意空指標異常。 虛引用:Phantom Reference,是極弱的一種引用關係,為一個物件設定虛引用的唯一目的就是希望能在這個物件被回收時收到一個系統通知。 5、ThreadLocal 執行緒使用ThreadLocal有三個重要方法: (1)set(): 如果沒有set操作的ThreadLocal容易引起髒資料問題。 (2)get(): 始終沒有get操作的ThreadLocal物件是沒有意義的。 (3)remove(): 如果沒有remove()操作,容易引起記憶體洩露。 ThreadLocal通常用於同一個執行緒內,跨類、跨方法傳遞資料 2020/11/15: 1、使用Thread Local和InheritableThreadLocal透傳上下文時,要注意執行緒間的切換、異常傳輸時的處理,避免在傳輸過程中因處理不當而導致上下文丟失。 2、ThreadLocal可能會導致:髒資料、記憶體洩漏。解決這兩個問題,需要在每次用完ThreadLoca時,必須要呼叫remove()方法清理。 JVM: 3、Java的類載入器是一個執行時的核心基礎設施模組。主要在啟動之初進行類的Load、Link和Init,即載入、連結、初始化 載入:Load階段讀取類檔案產生的二進位制流,並轉化為特定的資料結構,初步校驗cafe babe魔法數、常量池、檔案長度、是否有父類等,然後建立對應的Java.lang.class示例 連結:link階段包括驗證、準備、解析三個步驟。驗證是更詳細的校驗。比如final是否合規、型別是否正確、靜態變數是否合理等;準備階段是為靜態變數分配記憶體,並設定預設值,解析類和方法確保類與類之間的相互引用正確性,完成記憶體結構佈局。 初始化階段:執行類構造器<clinit>方法,如果賦值運算是通過其他類的靜態方法來完成的,那麼馬上會解析另外一個類,在虛擬機器棧重執行完畢後通過返回值進行賦值。 4、類載入是一個將.class位元組碼檔案例項化成Class物件並進行相關初始化的過程。在這個過程中,JVM會初始化繼承樹上還沒有被初始化過的所有父類,並且會執行這個鏈路上所有未執行過得靜態程式碼塊、靜態變數賦值語句等。某些類在使用時,也可以按需由類載入器進行載入。 5、記憶體佈局 (1)Heap(堆區),是OOM故障最主要的發源地。 堆分成兩大塊:新生代和老年代 新生代=一個Eden區+2個Survivor區。絕大部分物件在Eden區生成,當Eden區裝填滿的時候,會觸發Young Garbage Collection,即YGC。在Eden區實現清除策略,沒有被引用的物件則直接回收,依然存活的物件會被移送到Survivor區。 如果Survivor區無法放下該物件,或者超大物件的閥值超過上限,則嘗試在老年代中進行分配,如果老年代也無法放下,則會觸發Full Garbage Collection,即FGC。如果依然無法放下,則丟擲OOM (2)Metaspace(元空間) 在JDK8版本中元空間前生Perm區已經被淘汰。Perm區中的所有內容中字串常量移至堆記憶體,其他內容包括類元資訊、欄位、靜態屬性、方法、常量等都移至元空間內。 (3)JVM Stack(虛擬機器棧) 棧幀在整個JVM體系中的地位頗高,包括區域性變量表、操作棧、動態連結、方法返回地址等 a、區域性變量表是存放方法引數和區域性變數的區域 b、操作棧 c、動態連結 d、方法返回地址:方法執行時有兩種退出情況:正常退出,異常退出。 方法退出的過程相當於彈出當前棧幀,退出可能有3種方式: 1)返回值壓入上層呼叫棧 2)異常資訊拋給能夠處理的棧幀 3)pc計數器指向方法呼叫後的下一條指令 (4)Native Method Stacks(本地方法棧) (5)程式計數器 最後:從執行緒共享的角度來看,堆和元空間是所有執行緒共享的,而虛擬機器棧、本地方法棧、程式計數器是執行緒內部私有的 6、物件建立過程: (1)、確認類元資訊是否存在。當Java接收到new指令時,首先在metaspace內檢查需要建立的類元資訊是否存在。若不存在,那麼在雙親委派模式下,使用當前類載入器以,ClassLoader+包名+類名為KEY進行查詢對應的.class檔案。如果沒有找到則丟擲ClassNotFoundException異常,如果找到生成對應class類物件。 (2)、分配物件記憶體。首先計算物件佔用空間大小,如果例項成員變數是引用變數,僅分配引用變數空間即可,即4個位元組大小,接著在堆中劃分一塊記憶體給新物件。 (3)、設定預設值。成員變數的值都需要設定為預設值(即不同形式的0值) (4)、設定物件頭。設定新物件的hash碼,GC資訊,鎖資訊,物件所屬的類元資訊 (5)、執行init方法。初始化成員變數,執行例項化程式碼塊,呼叫類的構造方法,並把堆內物件的首地址賦值給引用變數。 2020/11/17: 1、垃圾回收,為了判斷物件是否存活,JVM引入GC Roots。如果一個物件與GC Roots之間沒有直接或者間接的引用關係,比如某個失去任何引用的物件,或者兩個互相環島迴圈引用的物件等。 2、垃圾回收演算法: (1)標記-清除演算法,會帶來大量的空間碎片,導致需要分配一個較大連續空間時容易觸發FGC (2) 標記-整理演算法,可以避免產生空間碎片 (3)Mark- copy演算法,堆記憶體空間分為較大的Eden和兩塊較小的Survivor,每次只使用Eden和Survivor區的一塊。(作為主流的YGC演算法進行新生代的垃圾回收) 3、垃圾回收器:Serial、CMS、G1三種 Serial主要用於YGC。 CMS回收器(Concurrent Mark Sweep Collector)是回收停頓的時間比較短、目前比較常用的垃圾回收器(採用標記-清除演算法,因而產生大量的空間碎片) G1(Garbage- First Garbage Collector),JDK7推出。採用“Mark- Copy”演算法,有非常好的空間整合能力,不會產生大量的空間碎片。 異常: 4、throw和throws區別: throw是方法內部丟擲具體異常類物件的關鍵字,throws則用在方法的signature上,表示方法定義者可以通過此方法宣告向上丟擲的異常物件。 5、無論採用哪種方式處理異常,都嚴禁捕獲異常後什麼都不做或列印一行日誌了事。如果在方法內部處理異常,需要根據不同的業務場景進行定製處理,如重試、回滾等操作。如果向上丟擲異常。需要在異常物件中加入上下文引數、區域性變數、執行環境等資訊,有利於問題的排查,示例程式碼如下: try{}catch(Exception e){ throw new DaoException("Sequence error, userId, userId="+userId, e); } 6、Exception分為checked異常(受檢異常)和unchecked異常(非受檢異常) checked異常是需要在程式碼中顯示處理的異常,否則會編譯出錯 unchecked異常可分為3類: (1)可預測異常,如Index Outof Bounds Exception、Null Pointer Exception,基於對程式碼效能和穩定性要求,此類異常不應該被產生或者丟擲,而應提前做好邊界檢查,空指標判斷等處理。顯示地宣告或者捕獲此類異常會對程式的可讀性和執行效率產生很大的影響。 (2)需捕捉異常(Caution Exception),如dubbo框架進行RPC呼叫時產生的遠端服務超時異常,此類異常是客戶端必須顯示處理的異常,不能因服務端的異常導致客戶端不可用,此時處理方案可以是重試或者降級處理等。 (3)可透出異常(Ignored Exception),主要是指框架或系統產生的切回自行處理的異常,而程式無需擔心。 7、try程式碼塊 如果沒有進入try程式碼塊,可能有三種情況: (1)沒有進入try程式碼塊 (2)進入try程式碼塊,但是程式碼執行中出現了死迴圈或者死鎖狀態 (3)進入try程式碼塊,但是執行了System.exit()操作。 注意⚠️: (1)finally是在return表示式執行之後執行的,此時將要return的結果已經被暫存起來,待finally程式碼塊執行結束後再將之前暫存的結果返回。 (2)finally的職責不在於對變數進行賦值等操作,而是清理資源、釋放連線、關閉管道流等操作,此時如果有異常也要做try-catch。 2020/11/18 1、異常,綜合考慮,日誌檔案推薦儲存15天,可以根據日誌檔案的重要程度、檔案大小及磁碟空間再自行延長儲存時間 2、5種不同級別的日誌按照重要程度由低到高排序: (1)DEBUG級別日誌記錄對除錯程式有幫助 (2)INFO級別日誌 (3)WARN級別日誌 (4)ERROR級別日誌 (5)FATAL級別日誌 3、注意事項: (1)日誌列印使用佔位符{}形式 (2)避免無效日誌列印,生產環境禁止輸出DEBUG日誌且有選擇地輸出INFO日誌 (3)區別對待錯誤日誌,WARN、ERROR都是與錯誤有關的日誌級別。ERROR級別只記錄系統邏輯錯誤、異常或者違反重要的業務規則,其他錯誤都可以歸為warn級別。 (4)保證記錄內容完整(需要記錄現場上下文資訊與異常堆疊資訊) a: 記錄異常時一定要輸出異常堆疊,例如:logger.error("xxx"+e.getMessage(),e) b: 日誌中如果輸出物件例項,要確保例項類重寫了toString方法,否則只會輸出物件的hashCode值,沒有實際意義 4、記錄日誌時一定要思考3個問題: (1)日誌是否有人看 (2)看到這條日誌能做什麼 (3)能不能提升問題的排查效率 5、日誌框架:分為3大部分,包括日誌門面、日誌介面卡、日誌庫。利用門面設計模式,即Facade來進行解耦,使日誌使用變得更加簡單。 (1)日誌門面:slf4j和commons- logging (2)日誌庫:logback是最晚出現的,它與log4j出自同一個作者,是log4j的升級版本且本身就實現了slf4j的介面 (3)如果是新工程,推薦使用slf4j+logback模式。因為logback本身實現了slf4j介面,無需額外引入介面卡。 6、private static final Logger logger = LoggerFactory.getLogger(Abc.class); 注意,logger被定義為static變數是因為這個logger與當前類繫結,避免每次都new一個新物件,造成資源浪費 ,甚至引發OutOfMemoryError問題。