JVM是跨平臺跨語言的虛擬機,不直接接觸硬件,位于操作系統的上一層
跟字節碼文件直接關聯,和語言沒有關系
一次編譯成字節碼文件,多次執行
虛擬機可以分成三部分:類加載器,運行時數據區,執行引擎(解釋器+JIT編譯器)
JIT編譯器是把一些常用代碼編譯成 機器指令,并緩存起來,加快執行速度【解釋器相當于步行,響應快但是速度慢。JIT要先編譯成機器指令,相當于公交車,要等一段時間,但是速度塊。只用解釋器很慢,只用JIT要等比較久】
零地址指令是因為棧里面每次只會有一個元素在棧頂,一次只處理一個
棧式架構指令集小,但是完成一個操作用的指令數量多
反編譯:運行寫的程序之后,會輸出編譯后的文件夾,cmd,cd進到某class文件的文件夾,javap -v class文件名 > xxx.txt 【就可以把字節碼文件的信息放到txt文件看】
一個Java程序對應一個java虛擬機(應該是)(服務器部署多個服務,應該有多個虛擬機)所以不同程序的堆棧不共享
第一部分:類加載器
- 加載:獲取類的全類名,讀取class文件,在方法區創建對應的Class對象
- 鏈接
- 驗證:驗證字節碼文件是否合法之類
- 準備**:把類變量(加了static的變量)創建處理,賦零值**
- 【加了final的常量在編譯時就已經分配內存,在準備階段顯式初始化】
- 【不會為實例變量分配初始化,實例變量,就是成員變量,是在對象創建時,分配到堆時,默認賦零值】
- 解析:把常量池內的符號引用換成直接引用【事實上,解析操作往往在JVM初始化之后執行】
- 初始化:執行類構造器方法的過程。就是如果有類變量或者靜態方法塊,虛擬機會自動把他們整合在一起,按順序創建變量和賦值,由字節碼中的方法執行,這個方法稱為類構造器方法
- 子類如果有類變量或者靜態方法塊,會先執行父類的方法,然后執行子類的
- 多線程下,會給方法加鎖
- 是類構造器,每個類的字節碼文件都有
- 通過 某個類.class.getClassLoader() 可以得到它的類加載器
- 獲取的類加載器對象.getParent() 可以獲得它包含的類加載器
- 引導類加載器
- 用C/C++寫,不能獲取到
- 加載Java的核心類庫(String類就是用它加載)
- 擴展類加載器
- 用Java寫
- 加載ext文件夾下的類庫
- 系統類加載器
- 用Java寫
- 默認的類加載器,加載環境變量或系統屬性
- 好處:
- 避免用戶創建自己的類替換Java的一些核心類,比如String
- 【如果包名和核心api包名一樣,即使是新的類,也不允許創建】
- 【實踐中,如果新建一個String類,這個類的包也是 java.long 里面有個main方法。但是啟動不了會報錯】
- 因為它經過雙親委派機制,到達引導類加載器,但是引導類加載器加載的是api里面的String,加載不了這個類,所以就啟動不了main方法
- 避免類的重復加載:向上委托,一旦父加載器加載了,子加載器就不會重復加載
- 不同的類加載器加載的類 就算包名一樣且同名也是不同的類(也是說要是從同一個class文件加載,不同類加載器,類也不同)
- 避免用戶創建自己的類替換Java的一些核心類,比如String
常用調優工具
- JDK命令行
- Eclipse: Memory Analyzer Tool
- Jconsole
- VisualVM
- Jprofiler
- Java Flight Recorder
- GCViewer
- GC Easy