1. 程式人生 > 遊戲攻略 >《破曉傳說》可治癒NPC所在位置一覽 慈愛的女神技能解鎖方法

《破曉傳說》可治癒NPC所在位置一覽 慈愛的女神技能解鎖方法

類載入過程

載入

  1. 取二進位制流:通過一個類的全限定名獲取定義此類的二進位制流。
  2. 轉換為執行時的資料結構:將位元組流靜態儲存結構轉化為方法去執行時的資料結構
  3. 生成class:在記憶體中生成一個代表這個類的class物件 ,作為方法區這個類的各種資料的訪問入口

驗證

  1. 檔案格式驗證:位元組流進入嫩村的方法區進行儲存
  2. 元資料驗證
  3. 位元組碼驗證:使用型別檢查來完成資料流分析校驗
  4. 符號引用驗證:

準備

正式為類變數分配記憶體並設定類變數初始值,都將在方法區中進行分配

解析

虛擬機器將符號引用替換為直接引用的過程

初始化

<clinit> <init>

六種觸發初始化的情形如下:

  1. new建立類例項
  2. 訪問某個類的或者介面的靜態變數,或者對該靜態變數賦值
  3. 呼叫類的靜態方法
  4. 反射class.forName("com.xxx.xxx")
  5. 初始化一個類的子類
  6. java虛擬機器啟動時被標名為啟動類的類

動態呼叫

靜態分配

對應方法的過載:依賴靜態型別來定位方法執行的版本,屬於多分派

動態分配

對應方法重寫,屬於單分派。

確認方法的步驟:

  1. 找到運算元棧的第一個元素所指向的物件的實際型別,即new 關鍵字後面型別
  2. 如果在常量池中找到描述符合簡單方法名都相符的方法,則進行訪問許可權的校驗通過,則直接引用
  3. 否則按照繼承關係自下而上進行第二部操作,重新查詢其他子類
  4. 如果始終沒有找到則丟擲異常

幾個相關名詞的定義:

宗量:方法接受者與方法的引數
單分派:根據一個總量對目標方法進行選擇
多分派:根據多個宗量對目標方法進行選擇

JAVA語言是一門靜態多分派 動態單分派語言

自旋:自旋是指某執行緒需要獲取鎖,但該鎖已經被其他執行緒佔用時,該執行緒不會被掛起,而是在不斷的消耗CPU的時間,不停的試圖獲取鎖。

偏向鎖

引入偏向鎖是為了在無多執行緒競爭的情況下儘量減少不必要的輕量級鎖執行路徑,因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令。
當只有一個執行緒去競爭鎖的時候,我們不需要阻塞,也不需要自旋,因為只有一個執行緒在競爭,我們只要去判斷該偏向鎖中的ThreadID是否為當前執行緒即可。如果是就執行同步程式碼,不是就嘗試使用CAS修改ThreadID,修改成功執行同步程式碼,不成功就將偏向鎖升級成輕量鎖。

輕量鎖

獲取輕量鎖的過程與偏向鎖不同,競爭鎖的執行緒首先需要拷貝物件頭中的Mark Word到幀棧的鎖記錄中。拷貝成功後使用CAS操作嘗試將物件的Mark Word更新為指向當前執行緒的指標。如果這個更新動作成功了,那麼這個執行緒就擁有了該物件的鎖。如果更新失敗,那麼意味著有多個執行緒在競爭。
當競爭執行緒嘗試佔用輕量級鎖失敗多次之後(使用自旋)輕量級鎖就會膨脹為重量級鎖,重量級執行緒指標指向競爭執行緒,競爭執行緒也會阻塞,等待輕量級執行緒釋放鎖後喚醒他。

重量鎖

重量級鎖的加鎖、解鎖過程和輕量級鎖差不多,區別是:競爭失敗後,執行緒阻塞,釋放鎖後,喚醒阻塞的執行緒,不使用自旋鎖,不會那麼消耗CPU,所以重量級鎖適合用在同步塊執行時間長的情況下

synchronized 和 volatile

  1. volatile本質是在告訴jvm當前變數在暫存器(工作記憶體)中的值不確定,需要從主記憶體中讀取;synchronize鎖定當前變數,只有當前執行緒可以當問該變數,其他執行緒被阻塞住
  2. volatile僅能使用在變數級別 synchronize可以使用在變數、方法、類
  3. volatile僅實現變數的修改可見性,不能保證原子性;而synchronized則都可以保證
  4. volatile不會造成執行緒的阻塞;synchronized會造成執行緒的阻塞
  5. volatile變數不會被編譯器優化;synchronized標記的變數可以被編譯器優化

synchronize 和 ReentrantLock

ReentrantLock擴充套件了synchronized
ReentrantLock可以對鎖的等待事件進行設定,這樣就避免了死鎖
ReentrantLock可以獲取各種鎖的資訊
ReentrantLock可以靈活地實現多路通知

兩者加鎖機制不一樣:
ReentrantLock底層呼叫的是Unsafe的park方法加鎖
synchronized操作物件頭中的mark word資訊進行加鎖

雙親委派模型

類載入器分類:每一個類載入器都有一個獨立的名命空間

啟動類載入器:是虛擬機器自身的一部分,用來載入JAVA_HOME/lib/目錄中的類
擴充套件類載入器:負責載入java.ext.dirs系統變數指定的路徑中所有的類庫
應用程式類載入器:負責載入使用者類路徑上的指定類庫,預設使用的類載入器

如果一個類載入器收到了類載入的請求,它首先不會去載入這個類,而是把這個請求委派給父類載入器去完成,每一層的載入器都是如此,這樣所有的載入請求會被傳到頂層的啟動類載入器中,只有當父類載入無法完成載入請求時,子載入器才會嘗試去載入類

類的例項化 時父子類中各屬性載入順序

  1. 父類靜態變數
  2. 父類靜態程式碼塊
  3. 子類的靜態變數
  4. 子類靜態程式碼塊
  5. 父類非靜態變數
  6. 父類建構函式
  7. 子類非靜態變數
  8. 子類建構函式
  9. 靜態變數 靜態程式碼塊 非靜態變數 建構函式

優化GC:

  1. 適當調整-XX:ServivorRatio的比例
  2. 選擇適合自己業務的垃圾收集器,web服務一般是ParNew+CMS
  3. 調整jvm老年代,新生代以及持久代的比例,測試出一個比較滿意的值
  4. 設定-XX:MaxTenuringThreshold 讓新生代提前進入老年代,減少在survivor區域的複製
  5. 調整 -XX:CMSInitiatingOccupancyFraction=60,控制minor gc頻率
  6. -XX+UseCMSCompactAtFullCollection消除cms碎片

垃圾回收以及演算法

引用計數法:當有一個地方使用計數值+1,失效時-1,為0時是不可再被引用的物件
缺點:迴圈引用時,某些物件將無法被回收掉

可達性分析演算法:通過一系列的稱為GCROOTS的物件作為起點,往下搜尋(路徑為引用鏈),當物件不與GC任何引用鏈相連時,則這些物件是不可達的。
GCROOTS物件包括:

  1. 虛擬機器棧中引用的物件
  2. 方法區中靜態屬性或者常量引用的物件
  3. 本地方法引用的物件

垃圾收集演算法:

  1. 標記清除(造成碎皮空間)
  2. 複製演算法 (記憶體使用率50%)
  3. 標記整理 (比標記清除多一步,將存活物件移動到一端,清除其他的物件)
  4. 分代演算法 (新生代:複製演算法 老年代:標記清除或者標記整理)

垃圾收集器:
ParNew(多執行緒 高吞吐)

CMS(初始標記,併發標記(時間長),重新標記,併發清除(時間長)低延遲)

CMS垃圾收集過程圖

G1(將整個堆劃分為一個個小塊,1-32M,RememberSet指向塊的記憶體地址) 調整小 -XX:InitiatingHeapOccupancyPercent=45% 增多Minor GC頻率,減少Full GC頻率

G1垃圾收集過程圖

參考文獻
^1 周志明 《深入理解JAVA虛擬機器》 第二版
TIPS: 轉載必須註明原文地址 和 引用參考文獻部分