筆記
-
JVM啟動流程
啟動過程如下圖所示:
bootup.png
注釋:
- jvm.cfg的用途:
Controls the JVMs which may be picked with startup flags when invoking java or javac。
當(dāng)前系統(tǒng)的默認(rèn)配置是:
```bash # List of JVMs that can be used as an option to java, javac, etc. # Order is important -- first in this list is the default JVM. # NOTE that this both this file and its format are UNSUPPORTED and # WILL GO AWAY in a future release. # # You may also select a JVM in an arbitrary location with the # "-XXaltjvm=<jvm_dir>" option, but that too is unsupported # and may not be available in a future release. # -server KNOWN -client IGNORE ```- JVM.dll
在linux上是libjvm.so,路徑:/usr/lib/jvm/java-8-oracle/jre/lib/amd64/server/libjvm.so。并且沒有client目錄,表面在linux都是server模式。
- jvm.cfg的用途:
-
JVM基本結(jié)構(gòu)
總的結(jié)構(gòu)示意圖如下所示:
structure.png
說明:- PC寄存器
每個線程擁有一個,指向下一條指令的地址,執(zhí)行native方法的時值為undefined。
這表明線程是執(zhí)行指令和CPU調(diào)度的基本單位。再復(fù)雜的系統(tǒng),也是從main線程擴展起來的。 - 方法區(qū)
JDK7放在Perm區(qū),JDK8放在MetaSpace區(qū)。
主要用于存放加載的類信息,包括:
1. 類型的常量值(JDK8已經(jīng)移到Heap上)。
2. 類的屬性(字段)和方法信息。
3. 方法字節(jié)碼。 - Java堆
- 應(yīng)用新建的對像、數(shù)組以及常量等都存放在Java堆上。
- Java堆可以被所有線程訪問,是最重要的內(nèi)存共享區(qū)域。
- 在分代GC算法下,Java堆是分代的。典型的分代如下圖:
heap_gen.png
- Java棧
- 線程私有。
- 存放方法調(diào)用相關(guān)的數(shù)據(jù),包括參數(shù)、局部變量以及返回值。對象、數(shù)組這些分配在堆上,棧里只保留引用。
- 棧上也可以分配對象。
前提條件:對象很?。◣资産yte);開啟逃逸分析(-XX:+DoEscapeAnalysis)
優(yōu)點:對象在不逃逸的情況下,直接分配在棧上,方法調(diào)用結(jié)束對象即回收,不增加堆的負(fù)擔(dān)。
缺點:要求對象很小,適用場景有限,用處不大,所以這項技術(shù)也沒有流行起來。
驗證:跑了資料中的代碼,在啟用逃逸分析后,只發(fā)生了很少幾次GC。如果禁用逃逸分析,會發(fā)生大量的GC。
開啟逃逸分析:public class OnStackTest { public static void alloc(int n) { byte[] b = new byte[n]; b[0] = 1; } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 500000000; i++) { alloc(2); } } }
禁用逃逸分析:dalton@fish ~$ java -Xmx10m -Xms10m -XX:+DoEscapeAnalysis -XX:+PrintGC OnStackTest [GC (Allocation Failure) 2048K->376K(9728K), 0.0012055 secs] [GC (Allocation Failure) 2424K->360K(9728K), 0.0018963 secs]
默認(rèn)會開啟逃逸分析的哦dalton@fish ~$ java -Xmx5m -Xms5m -XX:-DoEscapeAnalysis -XX:+PrintGC OnStackTest [GC (Allocation Failure) 1024K->408K(5632K), 0.0091321 secs] [GC (Allocation Failure) 1432K->352K(5632K), 0.0016615 secs] [GC (Allocation Failure) 1376K->352K(5632K), 0.0011853 secs] [GC (Allocation Failure) 1376K->352K(5632K), 0.0017079 secs] [GC (Allocation Failure) 1376K->368K(5632K), 0.0017921 secs] [GC (Allocation Failure) 1392K->352K(5632K), 0.0010113 secs] [GC (Allocation Failure) 1376K->292K(5632K), 0.0010523 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0020685 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0019285 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0020571 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0030951 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0014476 secs] [GC (Allocation Failure) 1316K->292K(5632K), 0.0007672 secs]dalton@fish ~/a_dev/d_java/perf $ java -Xmx10m -Xms10m -XX:+PrintGC OnStackTest [GC (Allocation Failure) 2048K->392K(9728K), 0.0012030 secs] [GC (Allocation Failure) 2440K->384K(9728K), 0.0018770 secs]
- PC寄存器
-
內(nèi)存模型
內(nèi)存模型,即我們常說的JMM,描述了內(nèi)存共享變量的讀寫及可見性。要點:- 每個線程都有自己的工作內(nèi)存。
- 共享變量存放在主內(nèi)存中,與各線程的工作內(nèi)存獨立。
-
線程要讀取或?qū)懭牍蚕碜兞浚髯远家?jīng)過另個步驟。
讀:read,load。read是從共享內(nèi)存中讀到工作內(nèi)存;load是從工作內(nèi)存中加載到變量。
寫:store,write。store是從變量保存到工作內(nèi)存;write是從工作內(nèi)存寫到共享內(nèi)存。
這個過程的示意圖如下:
jmm.png - 要做到線程間可見(或同步),可以用以下幾種方法:
- 用volatile關(guān)鍵字修飾變量。
- 用synchronized關(guān)鍵字修飾方法或代碼塊。線程在進入synchronized代碼塊時會從共享內(nèi)存中讀取變量值,離開時會寫入變量值。這就保證了執(zhí)行完synchronized代碼塊后對共享變量的修改是可見的。
- 使用常量。
-
指令重排
編譯器和執(zhí)行器都會基于一定的優(yōu)化原則對指令進行重排。其結(jié)果是指令的實際執(zhí)行順序不一定是我們代碼中看到的順序。重排是一種重要的優(yōu)化手段,但同時也增加了線程間同步的困難。因重排的條件比較復(fù)雜,我們倒是可以記住不發(fā)生重排的幾種情況:- 寫后讀
- 讀后寫
- 寫后寫
編譯器不考慮多線程間的語義。即它不會考慮多線程間的同步。
指令重排的基本原則:
- 程序順序原則:一個線程內(nèi)保證語義的串行性
- volatile規(guī)則:volatile變量的寫,先發(fā)生于讀
- 鎖規(guī)則:解鎖(unlock)必然發(fā)生在隨后的加鎖(lock)前
- 傳遞性:A先于B,B先于C 那么A必然先于C
- 線程的start方法先于它的每一個動作
- 線程的所有操作先于線程的終結(jié)(Thread.join())
- 線程的中斷(interrupt())先于被中斷線程的代碼
- 對象的構(gòu)造函數(shù)執(zhí)行結(jié)束先于finalize()方法
編譯與解釋運行的概念
解釋運行:讀一句執(zhí)行一句字節(jié)碼。
編譯運行:將字節(jié)碼編譯成機器碼,然后執(zhí)行。



