JVM的相關知識還是挺多的~~~讀書後特地都記下來~~~要不很容易就忘掉額。。。
一個JVM實例包括一個方法區,棧內存,堆內存,本地方法區,PC寄存器。
執行引擎(作用:解析jvm字節碼指令),每一個執行引擎實例是一個java線程。一個jvm實例中會同時有多個執行引擎在工作,這些引擎有的在執行用戶程序,有的在執行jvm內部的程序(如gc)
每創建一個執行引擎實例會給這個實例創建一個java棧和pc寄存器;如果當前正在創建一個java方法,那麼當前的java棧中會保存:該線程中方法調用的狀態(方法的參數,方法的局部變量,方法的返回值和運算中的中間結果)
如下面的這個方法:
public int hello(int a,int b){ int x = a; int y = b; int sum = x+y; return sum; }
那麼我們的棧中就會保存:方法的參數a和b,局部變量x和y,中間結果sum=x+y,和返回的sum值。
如果我們是在main函數中調用的話,假設邏輯如下:
public static void main(String []args){ int a = 4,int b = 8; int sum = hello(a,b); System.out.println(sum); }
PC寄存器執行到hello(a,b)這個函數後,出棧以後指向即將執行的下一條指令println();
注意:如果是在本地方法調用則存儲在本地方法調用棧中或者特定實現中的某個內存區域中。
在再往下面舉例之前先好好地總結一下一些簡單的知識點:
1.方法區(用於存儲類的結構信息):
常量池、域、方法數據、方法體、構造函數、包括類中的專用方法、實例初始化、接口初始化都存儲在這個區。
這個就是我們常說的java堆中的永久區。這個區域可以被所有線程共享,並且它的大小可以通過參數設置。
會佔用java堆的內存,受gc控制。
存放裝載類的數據信息包括:
(1)基本信息:
1.每個類的全限定名
2.每個類的直接超類的全限定名
3.該類是類還是接口
4.該類的訪問修飾符
5.直接超接口的全限定名的有序列表
(2)每個已裝載類的詳細信息
1.運行時常量池:
存放該類型所用的一切常量(直接常量和對其他類型、字段、方法的符號引用),它們以數組形式通過索引被訪問。
2.字段信息
類中聲明的每一個字段的信息(名,類型,修飾符)
3.方法信息
類中聲明的每一個方法的信息(名,返回類型,參數類型,修飾符,方法的字節碼和異常表)
4.靜態變量
5.到類classloader(類裝載器)的引用
6.到類class的引用(jvm爲每個被裝載的類型創建一個class實例,用來代表這個被裝載的類)
2.棧內存(棧和線程是對應起來的,不是線程共享)
java棧內存以幀的形式存放本地方法的調用狀態(包括方法調用的參數,局部變量,中間結果等)。每調用一個方法就將對應該方法的方法幀壓入java棧,成爲當前方法幀。當調用結束(返回)時,就彈出該幀。
編譯器將源代碼編譯成字節碼(.class)時就已經將各種類型的方法的局部變量,操作數和棧大小確定並放在字節碼中,隨着類一併裝載如方法區。當調用方法時,通過訪問該方法區中的類的信息,得到局部變量以及操作數棧的大小。
在方法中定義的一些基本類型變量和對象的引用變量都在方法的棧內存中分配。當超過變量的作用域後,java會自動釋放掉爲該變量所分配的內存空間,該內存空間可以立即被另作他用。
棧內存構成:局部變量區,操作數棧,幀數據區組成。
幀數據區處理常量池解析、異常處理等
3.堆內存(堆會被所有線程共享,需要注意同步問題)
堆內存存放由new創建的對象和數組。在堆中分配的內存,有java虛擬機的自動垃圾回收器來管理。在堆中產生一個數組或對象後,還可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等於數組或對象在堆內存中的首地址,棧中的這個變量就成了數組或對象的引用變量。引用變量就相當於爲數組或對象起的一個名稱,以後就可以在程序中使用棧的引用變量來訪問堆中的數組或對象。
其中java的引用數據類型包括:類,接口,數組
關於引用類型的內存分配,覺得這篇blog寫得挺簡明易懂的~~可以參考一下
http://liu1227787871.blog.163.com/blog/static/205363197201263103320466/
4.棧內存和堆內存的比較
與c++不同,java自動管理棧和堆,程序員不能直接地設置棧和堆。java堆是動態分配內存大小的,生存期也不用事先告訴編譯器,因爲它是在運行時動態分配內存的。所以堆的存取速度較慢。
棧的優勢是存取速度很快,僅次於寄存器。但數據的大小和生存期是確定的。
棧中的數據可以共享的,具體表現爲:
int a = 3; int b = 3;
首先在棧中創建一個變量爲a的引用,然後查找棧中是否有3這個值,如果沒有,把3存進來,然後a指向3,接着處理int b = 3;創建完b的引用變量後,如果在棧中已經有3這個值。
如果再加上一句b =4;(如右圖,b的值不會影響a的值)
注意:這種數據的共享和兩個對象的引用同時指向一個對象的這種共享是不同的,因爲這種情況a的修改不會影響到b,它是由編譯器完成的。
5.本地方法棧內存
和調用的語言相關,如果調用的是一個c語言的方法則爲一個c棧,java通過java本地接口JNI(java native interface)來調用其他語言編寫的程序,在java裏面用native修飾符萊描述一個方法是本地方法。