JVM的類載入過程
每日一句
如果可以,請讓我們慢慢了解,慢慢喜歡。
每日一句
The frog in the well knows nothing of the great ocean.
井底之蛙,不知大海。
JVM 的類載入階段
JVM 的類載入分為五個階段:
1. 載入:被虛擬機器讀入記憶體
2. 驗證:驗證 Class 位元組流的資料是否遵守JVM的規定
3. 準備:正式為類變數(靜態變數)分配記憶體並設定初始值,並非程式碼中設定的值
4. 解析:將常量池中的符號引用解析為直接引用
5. 初始化:真正執行類中定義的java程式碼
載入
指 JVM 讀取 class 檔案,並且根據 Class 檔案描述建立 java.lang.Class 物件的過程。
類載入過程主要包含將 Class 檔案讀取到執行時區域的方法區內,在堆中建立 java.lang.Class 物件,並封裝類在方法區的資料結構的過程。
在讀取 Class 檔案是既可以通過檔案的形式讀取,也可以通過 jar 包、war 包讀取,還可以通過代理自動生成 Class或其他方式讀取
驗證
主要用於確保 Class 檔案符合當前虛擬機器的要求,保障虛擬機器自身的安全,只有通過驗證的 CLass 檔案才能被 JVM 載入
準備
主要工作是在方法區中為類變數分配記憶體空間並設定類中變數的初始值。
初始值指不同資料型別的預設值,這裡需要注意 final 型別的變數和非final型別的變數在準備階段的資料初始化過程不同
例如一個成員變數定義如下:
public static long value = 1000;
在以上程式碼中,靜態變數 value 在準備階段的初始值是0,將 value 設定為 1000 的動作是在物件初始化時完成的,因為 JVM 在編譯階段會將靜態變數的初始化操作定義在構造器中。
public static final int value = 1000;
則JVM在編譯階段後會為final型別的變數value生成其對應的ConstantValue屬性,虛擬機器在準備階段會根據ConstantValue屬性將value賦值為1000。
總結:靜態變數會賦兩次初值,準備階段賦零值,初始化時賦使用者設定值,而final變數會在準備階段一次性賦值完畢
解析
JVM 會將常量池中的符號引用替換為直接引用。
初始化
主要通過執行類構造器的
在一個類中既沒有靜態變數賦值操作也沒有靜態語句塊時,編譯器不會為該類生成
在發生以下幾種情況時,JVM不會執行類的初始化流程:
1. 常量在編譯時會將其常量值存入使用該常量的類的常量池中,該過程不需要呼叫常量所在的類,因此,不會觸發該常量類的初始化。
2. 在子類引用父類的靜態欄位時,不會觸發子類的初始化,只會觸發父類的初始化。
3. 定義物件陣列,不會觸發父類的初始化
4. 在使用類名獲取 Class 物件時不會觸發類的初始化
5. 在使用 Class.forName 載入指定的類時,可以通過 initialize 引數設定是否需要對類進行初始化
6. 在使用ClassLoader預設的loadClass方法載入類時不會觸發該類的初始化。
美文佳句
很多時候,事情的困境,常常是因為我們自己鑽了牛角尖,此時,你需要做的就是改變。
完美主義者可以放下執念,允許自己有普通人都會犯的小迷糊;職場媽媽可以直面現實,一個人永遠做不到家庭和事業的雙百分;承擔了過多工作任務的員工,可以嘗試向上級反映,尋求資源或調整目標……這些,都是我們應當並可以作出的改變。
正如這句話所說:世界上從來都沒有所謂的奇蹟,命運一直都掌握在我們自己手裡。想要改變自己的命運,最重要的就是改變自己。當你開始改變自己的時候,很多東西就跟著改變了。
下一次,當煩惱降臨時,不妨試試從自身找找問題。調整努力的方向和節奏,學會給心靈鬆綁,你會發現:很多事,其實沒什麼大不了。
面試題
@RestController 和 @Controller 有什麼區別?
@RestController
註解,在 @Controller
基礎上,增加了 @ResponseBody
註解,更加適合目前前後端分離的架構下,提供 Restful API ,返回例如 JSON 資料格式。當然,返回什麼樣的資料格式,根據客戶端的 "ACCEPT"
請求頭來決定。
SpringMVC工作原理?
1. 客戶端傳送請求到前端控制器 DispatcherServlet
2. DispatcherServlet 收到請求後,呼叫 HandlerMapping 處理器對映器,請求獲取 handler
3. 處理器對映器根據 url 找到具體的處理器,生成處理器物件以及處理器攔截器(如果有則生成)一併返回給 DispatcherServlet
4. DispatcherServlet 呼叫 HandlerAdapter 處理器介面卡
5. HandlerAdapter 經過介面卡呼叫 具體處理器(Handler,也叫後端控制器)
6. Handler 執行完成返回 ModelAndView
7. HandlerAdaper 將 Handler 執行結果 ModelAndView 返回給 DispatcherServlet
8. DispatcherServlet 將 ModelAndView 傳給 ViewResolver 檢視解析器 進行解析
9. ViewResolver 解析後返回具體 View
10. DispatcherServlet 對 view 進行 渲染檢視(即將模型資料填充至檢視中)
11. DispatcherServlet 響應使用者
HashMap、ConcurrentHashMap 和Hashtable有什麼區別?
HashMap | ConcurrentHashMap | Hashtable | |
執行緒是否安全,以及實現執行緒安全的方式 | HashMap不安全 | 執行緒安全,ConcurrentHashMap JDK1.7底層採用分段鎖,對整個桶數進行了分割分段(segment), 每一把鎖只鎖容器其中一部分資料,提高併發訪問率。 JDK 1.8 底層採用 Node陣列 + 連結串列 + 紅黑樹的結構實現, 併發控制使用了 synchronized 和 CAS 操作。 |
執行緒安全,底層採用synchronized 來保證執行緒安全, 直接是方法級別的加鎖, ConcurrentHashMap 雖然也是 synchronized 但它是對連結串列或者紅黑樹的頭節點進行加鎖,鎖的粒度更小 |
底層工作原理 | 底層採用的是 陣列 + 連結串列 | ConcurrentHashMap JDK 1.7 底層採用 分段的陣列 + 連結串列實現。 JDK 1.8 採用的是 陣列 + 連結串列/紅黑樹 |
底層採用的是 陣列 + 連結串列 |
空值問題 | HashMap允許使用null值(key和value)都可以。 但是這樣的鍵只有一個,可以有一個或多個鍵所對應的值為null |
HashTable不允許null值(key和value都不可以) | |
初始容量、擴容與預設負載因子 | HashMap 預設初始大小 16,每次擴容 2n,預設負載因子是0。75 | HashTable 預設初始大小為11,每次擴容 2n+1 |
你好,我是yltrcc,日常分享技術點滴,歡迎關注我:ylcoder