java之Jvm學習--JVM運行機制


JVM啟動流程

1.java虛擬機啟動的命令是通過java +xxx(類名,這個類中要有main方法)或者javaw啟動的。

2.執行命令后,系統第一步做的就是裝載配置,會在當前路徑中尋找jvm的config配置文件。

3.找到jvm的config配置文件之后會去定位jvm.dll這個文件。這個文件就是java虛擬機的主要實現。

4.當找到匹配當前版本的jvm.dll文件后,就會使用這個dll去初始化jvm虛擬機。獲得相關的接口。之后找到main方法開始運行。

上面這個過程的描述雖然比較簡單,但是jvm的啟動流程基本都已經涵蓋在里面了。

jvm的基本結構

類加載器子系統就是通常我們所說的ClassLoader類加載器,首先我們會通過ClassLoader加載到jvm的內存中去,本地方法區主要就是native的方法調用,這個我們不前不做關心,

pc寄存器

1.每個線程擁有一個pc寄存器。
jvm會為每一個線程分配一個pc寄存器,這個pc寄存器總是會指向下一個指令的地址。這樣程序在執行過程中pc寄存器總是會知道下一步會做什么。在執行本地方法的時候,pc寄存器的值總是未定義的。

方法區

方法區是用來保存類的原信息。用來描述類的信息,包括類型常量池,字段方法信息,方法字節碼。在JDK6的時候字符串常量是放在方法區中,但是JDK7的時候就已經移到了堆中。所以從這方面來說方法區,堆中到底保存的是什么信息和jdk的版本有很大的關系。從一般意義上來說我們的方法區就是保存一些類的原信息。方法區通常和永久區(perm)關聯在一起,保存一些相對穩定的數據,

java堆

1.java堆應該是和程序開發中最為密切的一個內存區間,我們在程序開發中通過new出來的對象基本上都是保存在java堆中。
2.堆是全局共享的,所有線程都共享java堆,也就是你創建了一個對象之后,所有的線程都是能夠訪問的。
3.從GC的角度看,java堆的結構和GC的算法是有關系的。

java棧

1.java棧和堆相比是線程私有的,棧是由一系列幀組成的,所以java棧也叫作幀棧。幀中保存的內容是一個方法的局部變量,操作數棧,常量池指針。每一次方法調用都會創建一個新的幀,並壓棧。
我們來看一段C++代碼
在上面代碼中method()這個方法中,我們new了一個對象,那這個new的對象就是在堆上分配的,但是在堆上分配有一個問題就是每次我們new一個對象之后都要手動把這個對象去刪除,釋放內存。如果我們多次在堆上分配了對象空間,但是卻忘記了刪除對象,就會出現內存泄露,就是我們分配空間卻沒有刪除。內存泄露在實際開發中是非常難以解決的問題,因為內存泄露有可能發生在任何地方。 
我們可以采用右面的方法,聲明一個對象,我們像上面右面的方法中聲明一個對象,那么他並沒有實際的划分內存空間,而只是在java棧上產生了一個引用。而這個引用在我們使用后會自動釋放,不會產生內存泄露的問題。

java棧上分配好處

棧上分配的一般都是比較小的對象,在沒有逃逸(逃逸是指這個對象創建出來之后不僅僅只在當前線程中要使用,其他的線程也要調用的情況)的情況下,直接分配到棧上。GC可以自動回收,減輕GC的壓力。大對象或者逃逸對象無法分配到棧上。
我們從上面的代碼和主時中可以交互,一個程序要想執行是需要幾個內存區域交互配合執行的。
從上面這個圖中我們可以發現,每個線程讀取和存儲的都是線程的工作內存。而線程的工作內存再到主存中的存儲是肯定會有一些時差的。也就是改變了一個變量的值之后,另一個調用這個變量的對象是不能馬上知道的。如果說要讓其他線程立即可見這個改動,就要使用volatile關鍵字修飾。一旦使用這個關鍵字之后,所有調用這個變量的線程就直接去主存當中拿取數據。
下面這個圖就是線程和本地內存和主存之間的關系。
 
線程總是在自己的本地內存中拿取變量,而本地內存中存儲的只是共享變量的一個副本,真正的共享變量是存儲在主存中的。所以這個之間存在了一定的時延和誤差。

可見性

可見性是指一個線程修改了變量之后,其他線程能夠立即知道。
保證可見性的方法就是上圖提到的三種方法。
 

有序性和指令重排

有序性:在一個線程當中,所有的指令,所有的操作都是有序的。但是在線程外觀察,在多線程的情況下去觀察前面一個線程的行為,我們會發現這個行為有可能就是無序的(這種無序有兩種原因,一種就是指令重排,另一種就是主存同步的延時,也就是說在線程A中更改了一個變量的值,同步主存也成功了,但是在線程B中我們可能還沒來得及去同步主存中的值,這個時候對於線程B來說線程A的操作可能就是無序的)。
指令重排的基本原則:
1.程序順序原則,一個線程內保證語義的串行性。
2.volatile規則:volatile關鍵字變量的寫是先發生於讀的。
3.鎖規則:解鎖必然發生於隨后的一個加鎖之前。
4.傳遞性:A先於B,B先於C,那么A必然先於C。
5.線程的start方法先於它的每一個動作。
6.線程的所有操作先於線程的終結(Thread.join())。
7.線程的中斷(interrupt())先於線程被中斷的代碼。
8.對象的構造函數執行結束於funlize()方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM