第一節 參考
一.openjdk7:
https://blog.csdn.net/hcj116/article/details/54946551
https://blog.csdn.net/j754379117/article/details/53695426
https://www.jianshu.com/p/e53e7964db03
http://www.txazo.com/jvm/openjdk-compile.html
https://super2bai.github.io/JVM/build.html
https://www.jianshu.com/p/5107fc72558f
http://www.cnblogs.com/zyx1314/p/5638596.html
https://stackoverflow.com/questions/6000554/clang-complete-where-is-the-libclang-so-dylib-in-os-xhttps://liuzhengyang.github.io/2017/04/28/buildopenjdk/
sudo ln -s /Users/feivirus/Documents/software/apache-ant-1.8.2/bin/ant /usr/bin
sudo ln -s /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib /usr/local/lib/
二.openjdk8:
https://blog.csdn.net/manageer/article/details/72812149
三.openjdk9:
https://juejin.im/post/5a6d7d106fb9a01ca47abd8b
https://segmentfault.com/a/1190000005082098
https://www.gonwan.com/2017/12/01/building-debugging-openjdk8-from-source-on-macos-high-sierra/
https://www.zhihu.com/question/52169710
process handle SIGSEGV --stop=false
https://medium.com/@maxraskin/background-1b4b6a9c65be
add-dsym /Users/feivirus/Documents/project/eclipse/jdk9/build/macosx-x86_64-normal-server-slowdebug/support/modules_libs/java.base/server/libjvm.dylib.dSYM
https://apple.stackexchange.com/questions/309017/unknown-error-2-147-414-007-on-creating-certificate-with-certificate-assist
第二節.openjdk源碼子產品
主要内容: 類二分模型,類加載,堆棧結構,解釋器的模闆表與轉發表,編譯器,函數分發指令集.
一.hotspot源碼結構
(一)vm根目錄下:
1.Adlc:平台描述檔案(cpu或os_cpu目錄中的.ad檔案)的編譯器
2.asm:彙編器
3.c1 Client編譯器
4.ci 動态編譯器的公共服務(從動态編譯器到VM的接口)
5.classfile 處理類檔案(包括類加載和系統符号表等)
6.code 管理動态生成的代碼
7.compiler 從VM調用動态編譯器的接口
8.gc_implementation GC實作
9.gc_interface GC接口
10.interpreter 解釋器,包括模闆解釋器(官方版使用)和C++解釋器(官方版未用)
11.libadt:抽象資料結構
12.memory 記憶體管理相關實作(老的分代式 GC 架構也位于此處)
13.oops HotSpot VM的對象系統的實作
14.opto Server編譯器(即C2)
15.prims HotSpot VM的對外接口,包括部分标準庫的native部分實作和JVMTI實作
16.rumtime 運作時支援庫(包括線程管理、編譯器排程、鎖、反射等)
17.services 用于支援JMX之類的管理功能的接口
18.shark 基于LLVM的JIT編譯器(官方版未用)
19.utilities 一些基本工具類
(二).prims對外接口子產品
主要包括jni,perf,jvm,jvmti四個子產品.
1.jni子產品(java native interface,允許java代碼與本地代碼互動,以 jni_字首)
jni知識參考 https://blog.csdn.net/column/details/blogjnindk.html
2.jvm子產品(對jni的補充,以jvm_字首)
涉及jvm.h檔案等.主要包含導出函數,比如通路jvm狀态,位元組碼和class檔案格式校驗,各種IO和網絡操作.
3.jvmti子產品
監控和調優java程式
4.perf子產品
以perf_為字首,監控虛拟機.
(三).services子產品
通過jmx監控和管理java應用。jmx參考https://www.cnblogs.com/dongguacai/p/5900507.html
分為Management,MemoryService,ThreadService,RuntimeService,MemoryManager,HeapDumper,ClassLoadingService,MemoryPool,AttachListener九個子子產品.
(四)Runtime子產品
分為Thread(線程隊列),Arguments(vm參數解析),StubRoutines/StubCodeGenerator(Stub例程),Frame棧幀(frame.hpp),CompilationPolicy(編譯政策),Init(系統初始化),VmThread子產品(全局單例線程,維護操作隊列VmOperationQueue,執行GC,對外提供監控), VMOperation(比如ThreadStop,FindDeadlocks,ForceSafepoint,ParallelGCSystemGC等),互斥鎖,安全點,PerfData,,反射,
二.虛拟機啟動
(一)虛拟機的生命周期
啟動器分為通用啟動器(Generic Launcher)和調試版啟動器(gamma)兩種.
通用啟動器就是java和javaw,差別是javaw沒有控制台視窗.gamma啟動器入口位于hotspot/src/share/tools/luncher/java.c中.通用啟動器入口在jdk/src/share/bin/main.c中(jdk9改在jdk/src/java.base/share/native/launcher下).
jdk/src/share/bin/main.c的main()->jdk/src/share/bin/java.c的JLI_Launch()->jdk/src/solaris/bin/java_md_solinux.c的JVMInit()建立線程調用JavaMain()->jdk/src/share/bin/java.c的JavaMain()(jvm啟動核心操作)在該方法中依次調用InitializeJVM()初始化jvm(此方法進入hotspot/src/share/vm/prims/jni.cpp的JNI_CreateJavaVM()中->hotspot/src/share/vm/runtime/thread.cpp中的Threads::create_vm()->hotspot/src/share/vm/runtime/vmThread.cpp的VMThread::create()),調用LoadMainClass()加載主類,調用GetStaticMethodID()擷取main方法的id号,調用CreateApplicationArgs()建立java的main方法的參數,調用CallStaticVoidMethod()進入java的main方法->javac中的DetachCurrentThread()->javac中的DestroyJavaVM()
啟動過程的堆棧如下圖
進入java代碼的main()方法前的堆棧如下圖:
其中StubRoutines::call_stub()是個函數指針,指向被調用的java的main函數位址.
(二)過程分析
1.main.c檔案的main方法,主要是擷取java程序參數,調用LoadJavaVM()加載libjvm,啟動新線程啟動JavaMain()方法.
第三節.常用類
主要是集合相關類,比如
Hashmap的紅黑樹,synchronized,proxy,serviceloader,threadpoolexecutor,classloader,aqs等.
第四節.垃圾回收算法
一.看垃圾回收的日志
2019-12-19T11:26:16.942-0800: 0.493: [GC (Allocation Failure) [PSYoungGen: 33264K->5110K(38400K)] 33264K->5950K(125952K), 0.0045817 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
2019-12-19T11:26:16.942:gc開始時間
-0800:中國所在的東8區
0.493:相對于JVM啟動時的間隔時間,機關是秒
GC:代表Minor GC。此字段還可以為Full GC.
Allocation Failure:觸發gc的原因.此字段還可以是Ergonomics,代表jvm的自動gc.
PSYoungGen:年輕代還是年老代.
33264K->5110K:gc之前和gc之後,年輕代的使用量
38400K:年輕代總的空間大小
33264K->5950K:gc之前和gc之後,整個堆記憶體的使用情況
125952K:可用的堆的總的空間大小
0.0045817 secs:gc持續時間大小
user,sys,real:分别是gc消耗時間和,系統調用時間,stw的時間.
二.不同垃圾回收算法的核心差別
1.gc的過程是标記->清除->整理.
2.垃圾收集器的種類
Serial/Serial Old:串行GC.最古老的,單線程收集器.年輕代使用mark-copy,年老代使用mark-sweep-compact(标記-清除-整理)算法.兩者都會觸發STW.設定-XX:+UseSerialGC.
ParNew:Serial的多線程版本
Parallel Scavenge:年輕代的多線程收集器,預設收集器,采用mark-copy算法,回收期間不stw.
Parallel Old:Parallel Scavenge收集器的年老版本,采用Mark-sweep-Compact算法.
CMS:采用Mark-Sweep算法,最短的stw時間.年輕代使用Parallel New,mark-copy算法.年老代使用CMS,mark-sweep算法.在Initial Mark初始标記和Final REMARK重新标記時會stw.缺點是老年代記憶體碎片問題,在某些情況下GC會造成不可預測的暫停時間,特别是堆記憶體較大的情況下.依次經曆Initial Mark->Concurrent Mark->Concurrent Preclean->Concurrent Abortable Preclean->Final Remark->Concurrent Sweep->Concurrent Reset過程.
G1:預測STW時間.
3.标記之後可以有清除,整理,複制三種處理方式.
清除的缺點是還有很多空閑記憶體, 卻可能沒有一個區域的大小能夠存放需要配置設定的對象, 進而導緻配置設定失敗(在Java中就是OutOfMemoryError).
整理的缺點就是GC暫停時間會增加, 因為需要将所有對象複制到另一個地方, 然後修改指向這些對象的引用。
複制的缺點則是需要一個額外的記憶體區間, 來存放所有的存活對象。
4.年輕代和老年代處理方式不同。年輕代的特點是時間短,小對象。年老代的特點是預設都是存活的,時間長,大對象.
預設的gc設定是-XX:+UseParallelGC.在年輕代使用Parallel Scavenge,年老代使用Parallel Old.
第五節.多線程,同步與可見性,記憶體模型(重排序)
一.線程同步
記憶體一緻性:對同一個變量,在實體記憶體中存一個,在不同的線程中,各有一份自己的緩存,需要保持這個變量的讀寫相等.
主要包括原子性,有序性,可見性.jvm自己的多線程的可見性.
緩存一緻性協定:cpu的L1,L2,每個cpu有自己的寫緩沖區,interl的mesi.主要是cpu硬體可見性.
二.重排序
編譯器重排序
處理器重排序:指令重排序,記憶體系統重排序
記憶體屏障指令禁止處理器重排序:LoadLoad,StoreStore,LoadStore,StoreLoad
三.happens-befores規則
四.volatile
1.源碼在jdk的LIRGenerator::volatile_field_load()方法中處理.
2.讀寫前後加記憶體屏障指令
3.寫volatile變量,線程本地變量會重新整理到主記憶體.讀volatile的變量,線程本地變量會置無效,從主記憶體讀取值.
五.final 也會加屏障,防止初始化時,讀到前後兩個值.
六.synchronized
1.分類:
鎖對象(普通方法,加ACC_SYNCHRONIZED修飾符),鎖class(靜态方法),鎖代碼塊(通過monitorenter,monitorexit指令實作).
2.實作方式:
(1).java對象頭的Mark Word有輕量級鎖,重量級鎖和偏向鎖的标志位.
(2).Monitor機制.每個線程有個monitor清單.
3.鎖
(1)種類:自旋鎖(預設自旋10次),适應性自旋鎖(根據上次自旋成功或失敗判斷),鎖消除,鎖粗化,偏向鎖,輕量級鎖.
(2)狀态:無鎖,偏向鎖,輕量級,重量級,鎖隻能更新不能降級.
七.AQS
1.使用CLH同步隊列.lock時,入隊列,目前節點變為尾節點,自旋判斷prev節點是否釋放鎖.unlock出隊時,把目前節點的鎖狀态釋放。這樣,後繼節點就能拿到鎖.
2.分為獨占式和共享式.
3.使用LockSupport,通過Unsafe阻塞或者喚醒線程.
八.ReentrantLock
1.排他鎖.繼承AQS,分為公平鎖,非公平鎖,FairSync和NonFairSync.
九.ReentrantReadWriteLock
1.讀寫鎖.可重入,支援公平性和非公平性.鎖降級(寫鎖降級為讀鎖,擷取寫,擷取讀,釋放寫).
2.讀鎖内部維護HoldCounter計數器。
十.condition
1.包含一個FIFO隊列,每個節點包含一個線程引用.調用await方法時加入隊列尾部.signal方法喚醒隊列的第一個節點線程.
十一.CAS
1.實作方式:通過處理器的LOCK#信号做總線加鎖.緩存加鎖.
2.問題:循環時間長.隻能保證一個共享變量原子操作.ABA問題(加版本号).
十二.CyclicBarrier
1.内部使用ReentrantLock和Condition.
十三.CountDownLatch
1.内部依賴Sync實作,Sync繼承自AQS.
2.await()在計數器到0之前一直等待.countDown()線程遞減計數器.
十四.Semaphore
可用數量,擷取到一個Semaphore,可用數量減1.Semaphore為0時,線程等待.
十五.Exchanger
Exchanger對象了解為一個包含兩個格子的容器,通過exchanger方法可以向兩個格子中填充資訊。
十六.ConcurrentHashMap
第六節.IO/NIO/AIO,reactor/proactor
第七節 對象結構,位元組碼與模闆解釋器
一.對象結構
(一)Object結構
在jdk1.8,mac 64位下,空對象占用16個位元組
Object = Header + Primitive Fields + Reference Fields + Alignment & Padding
1.對象頭Header
Header: mark word + Klass pointer
mark word:64位是8位元組,32位是4位元組.
Klass pointer:32位是4位元組,64位預設開啟UseCompressedOops是4位元組,不開啟8位元組.
空對象64位下是8+4=12 位元組.
2.成員屬性Primitive Fields + Reference Fields
空對象沒有這部分.不同資料類型大小不同,比如int 4 bytes,double 8 bytes,引用一個word大小.數組也是對象,header中有一個int類型的length值,多4個位元組.
3.對齊Alignment和補齊Padding
對齊:任何對象都是 8位元組對齊.比如空對象,12 bytes的header部分,對齊加4個位元組.
補齊:位元組補齊4個位元組.
(二)對象重排序
在heap中對field成員屬性進行重排序,節省空間.
配置設定順序原則:1.double > long > int > float > char > short > byte > boolean > object reference
2.Padding Size(4 bytes)的類型的優先級就高于大小>Padding Size的類型
3.子類和父類的field不混在一起,父類的field配置設定完再配置設定子類.
4.父類的的最後一個字段與子類的第一個字段以一個Padding Size(4 bytes)對齊
(三).Oop/Klass 模型
當在java中new一個對象時,在c++層面是在堆記憶體建立一個instanceOopDesc對象.instanceOopDesc對象通過中繼資料指針_metadata指向InstanceKlass中繼資料,比如方法區資訊.
1.oop:Ordinary Object Pointer,表示對象的執行個體資訊.class instanceOopDesc描述java層面的對象,就是對象頭.繼承自oopDesc.
class oopDesc {
...
private:
//對象頭資訊,markOop是個markOopDesc*類指針?但實際是個Word,8位元組,一個字長,存儲具體的資料
//markOopDesc在markOop.hpp中定義.
//成員變量有hashcode,gc分代年齡,鎖狀态标志等.
//在markOop.hpp中檔案注釋有說明各種标志位。通過各種位操作函數修改這裡面比特位的值.
volatile markOop _mark;
//聯合,隻存一種
union _metadata {
//InstanceKlass 對象的指針
Klass* _klass;
//壓縮指針
narrowKlass _compressed_klass;
} _metadata;
...
}
markOop部分檔案注釋如下:
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
2.Klass:存儲對應c++對象的虛表等類的中繼資料資訊.在元空間方法區.
InstanceKlass類,繼承自Klass類,在instanceKlass.cpp的最上方檔案注冊中畫出了InstanceKlass布局,包含
// InstanceKlass layout:
// [C++ vtbl pointer ] Klass
// [subtype cache ] Klass
// [instance size ] Klass
// [java mirror ] Klass
// [super ] Klass
// [access_flags ] Klass
// [name ] Klass
// [first subklass ] Klass
// [next sibling ] Klass
// [array klasses ]
// [methods ]
// [local interfaces ]
// [transitive interfaces ]
// [fields ]
// [constants ]
// [class loader ]
等.每個字段對應一個InstanceKlass的成員屬性.ClassFileParser把class檔案在運作時解析成InstanceKlass對象.Klass類中有對應java層面Setter/Getter方法的轉換函數.
二.位元組碼
三.模闆解釋器
核心三個檔案模闆解釋器templateInterpreter.cpp,模闆解釋器生成器templateInterpreterGenerator,模闆表templateTable.cpp.
解釋器分為兩種,彙編類型TemplateInterpreterGenerator和c++類型CppInterpreterGenerator,共同父類是AbstractInterpreter.
1.解釋器結構
每個模闆的定義在templateTable.hpp中的Template類中,屬性包含标志位,棧狀态,目标代碼等.
2.初始化過程
(1).位元組碼處理函數定義
在jvm啟動時調用TemplateTable::initialize(),對每一個位元組碼調用TemplateTable::def(),定義處理函數.def的generator列是對應的處理函數,不過這個是個宏定義.TosState是位元組碼執行前後的棧頂狀态.宏定義在templateTable.cpp中.比如#define istore TemplateTable::istore.
比如如果查找istore指令的處理方法,直接在TemplateTable.cpp或者對應的cpu架構下TemplateTable_xxx.cpp中搜尋 TemplateTable::istore就可以搜到對應處理函數.在templateTable_x86_64.cpp中代碼如下:
void TemplateTable::istore() {
transition(itos, vtos);
locals_index(rbx);
//存到局部變量表
__ movl(iaddress(rbx), rax);
}
(2).位元組碼處理函數使用
在Bytecodes::Code枚舉中定義了所有的可以用的位元組碼.在TemplateInterpreterGenerator::set_entry_points_for_all_bytes()方法中周遊Bytecodes::Code枚舉,對每個位元組碼枚舉調用TemplateInterpreterGenerator::set_entry_points()方法設定處理函數.這個方法的實作在templateInterpreter.cpp檔案中.set_entry_points()方法針對每個位元組碼生成兩種處理函數,分别調用TemplateTable::template_for()和TemplateTable::template_for_wide(),窄版和寬版.在這兩個方法裡面進入TemplateInterpreterGenerator::set_short_entry_points()中.調用會調用TemplateInterpreterGenerator::generate_and_dispatch().在這個方法中有個類似aop的操作。
void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
if (PrintBytecodeHistogram) histogram_bytecode(t);
#ifndef PRODUCT
// debugging code
if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) count_bytecode();
if (PrintBytecodePairHistogram) histogram_bytecode_pair(t);
if (TraceBytecodes) trace_bytecode(t);
if (StopInterpreterAt > 0) stop_interpreter_at();
__ verify_FPU(1, t->tos_in());
#endif // !PRODUCT
int step = 0;
if (!t->does_dispatch()) {
step = t->is_wide() ? Bytecodes::wide_length_for(t->bytecode()) : Bytecodes::length_for(t->bytecode());
if (tos_out == ilgl) tos_out = t->tos_out();
// compute bytecode size
assert(step > 0, "just checkin'");
// setup stuff for dispatching next bytecode
if (ProfileInterpreter && VerifyDataPointer
&& MethodData::bytecode_has_profile(t->bytecode())) {
__ verify_method_data_pointer();
}
//執行前的pro處理
__ dispatch_prolog(tos_out, step);
}
// generate template
//産生模闆代碼
t->generate(_masm);
// advance
//執行位元組碼
if (t->does_dispatch()) {
#ifdef ASSERT
// make sure execution doesn't go beyond this point if code is broken
__ should_not_reach_here();
#endif // ASSERT
} else {
// dispatch to next bytecode
//排程下一個位元組碼
__ dispatch_epilog(tos_out, step);
}
}
第八節 監控工具
一.工具
hsdb,aprof,gcviewer,hprof,hsdis,jconsole,jhat,jinfo,jmap,jstack,visualvm