根據馮·諾依曼思想,計算機採用二進制作爲數制基礎,必須包含:運算器,控制器,存儲設備,輸入輸出設備:
首先拋開主題,來分析分析cpu的基本工作原理:現代cpu芯片中大部分都集成了控制單元,運算單元,存儲單元。控制單元是cpu的控制中心,cpu需要通過控制單元才能知道下一步做什麼,也就是執行什麼樣的指令。控制單元又包括:
指令寄存器(IR),指令譯碼器(ID)和操作控制器(OC)。
當程序被加載進內存之後,指令就在內存中。指令指針寄存器IP指向內存中的下一條等待執行的指令的地址,控制單元根據IP寄存器的指向,將主存中的指令轉載到指令寄存器,這個指令寄存器也是一個存儲設備,不過是集成於CPU內部。
首先要明白指令從主存到達CPU後只是一串010111001的二進制串,還需要通過譯碼器進行解碼,分析出操作碼是什麼,操作數在哪裏?之後就是具體的運算單元來進行算術運算(加減乘除),邏輯運算。總之一句話:CPU指令執行的過程爲
取址(內存中取操作樹放入到寄存器)——>譯碼(從主存中獲取操作數放入高速緩存L1中)——>執行(運算)
所有的指令都會按照這個順序來進行執行。但是多個指令之間其實是可以並行的,對於單核CPU來說,同一個時刻執行單元最多隻能被一條指令所佔有。也就是上述中的執行只能串行執行。所以爲了提升CPU的指令執行速度。
需要保證在運算單元在執行前的準備工作都已經完成,這樣就不會使運算單元存在經常性的等待。而在剛剛的串行流程中,取指,解碼的過程中,運算單元處於空閒狀態,而且如果取指和解碼的過程中沒有命中,高速緩存還需要從主存中取,而主存的速度和CPU的速度不在一個級別上。所以,指令流水線可以大大的提高CPU的處理速度,如下圖,下圖是一個三級流水線的示意圖,現在的奔騰CPU基本上都是32級的流水線。
當然,除了指令流水,CPU還分支預測,亂序執行等優化手段,具體《計算機組成原理》中都有介紹。
那麼,一行java代碼是如何執行的?
一行代碼能夠執行,必須要有可以執行的上下文環境,包括,指令寄存器,數據寄存器,棧空間等內存資源,然後這行代碼必須作爲一個執行流能夠被操作系統的任務調度器識別,並給他分配 CPU 資源,當然這行代碼所代表的指令必須是 CPU 可以解碼識別的,所以一行 Java 代碼必須被解釋成對應的 CPU 指令才能執行.下面我們看下System.out.println(“Hello world”)這行代碼的轉譯過程.
Java 是一門高級語言,這類語言不能直接運行在硬件上,必須運行在能夠識別 Java 語言特性的虛擬機上,而 Java 代碼必須通過 Java 編譯器將其轉換成虛擬機所能識別的指令序列,也稱爲 Java 字節碼,之所以稱爲字節碼是因爲 Java 字節碼的操作指令(OpCode)被固定爲一個字節,以下爲 System.out.println(“Hello world”) 編譯後的字節碼
0x00: b2 00 02 getstatic Java .lang.System.out
0x03: 12 03 ldc "Hello, World!"
0x05: b6 00 04 invokevirtual Java .io.PrintStream.println
0x08: b1 return
最左邊是偏移,中間是給虛擬機讀取的字節碼,右邊是對應的高級語言代碼。下面是通過彙編語言轉換成的機器指令。中間是機器碼,第三列爲及其指令。最後一列對應彙編代碼。
0x00: 55 push rbp
0x01: 48 89 e5 mov rbp,rsp
0x04: 48 83 ec 10 sub rsp,0x10
0x08: 48 8d 3d 3b 00 00 00 lea rdi,[rip+0x3b]
; 加載 "Hello, World!
"
0x0f: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0
0x16: b0 00 mov al,0x0
0x18: e8 0d 00 00 00 call 0x12
; 調用 printf 方法
0x1d: 31 c9 xor ecx,ecx
0x1f: 89 45 f8 mov DWORD PTR [rbp-0x8],eax
0x22: 89 c8 mov eax,ecx
0x24: 48 83 c4 10 add rsp,0x10
0x28: 5d pop rbp
0x29: c3 ret
JVM 通過類加載器加載 class 文件裏的字節碼後,會通過解釋器解釋成彙編指令,最終再轉譯 成 CPU 可以識別的機器指令,解釋器是軟件來實現的,主要是爲了實現同一份 Java 字節碼可 以在不同的硬件平臺上運行,
而將彙編指令轉化成機器指令由硬件直接實現,速度非常快,當然,大部分JVM爲了提高自身的運行效率,將某些熱點代碼(一個方法中的代碼)一次全部編譯成機器指令然後執行,也就是和解釋執行對應的即時編譯(JIT), JVM 啓動的時候可以通過 -Xint 和 -Xcomp 來控制執行模式.具體自己可以操作一波。
從軟件層面上看,class文件被加載進虛擬機後,類信息會存放在方法區,在實際運行的時候會執行方法區中的代碼,在JVM中的所有線程共享堆內存和方法區,而每個線程有自己獨立的java方法棧。
本地方法棧(面向 native 方法),PC寄存器(存放線程執行位置),當調用一個方法的時候, Java 虛擬機會在當前線程對應的方法棧中壓入一個棧幀,用來存放 Java 字節碼操作數以及局部變量,這個方法執行完會彈出棧幀,一個線程會連續執行多個方法,對應不同的棧幀的壓入和彈出,壓入棧幀後就是 JVM 解釋執行的過程了.
參考博文:
https://time.geekbang.org/column/article/11289
https://blog.csdn.net/u013635487/article/details/61413589
https://blog.csdn.net/dong_daxia/article/details/80289951