java虛擬機器學習筆記
來源:互聯網
上載者:User
筆記
1.編譯順序: 編譯器 虛擬機器 虛擬機器 java源檔案*.java------->位元組碼*.class------>類裝載器--->執行引擎
一個.class檔案只能包含一個類或介面。因此.java檔案中定義了多少類,編譯時間就會產生多少.class檔案(內部類不算)。
2.java程式可以選擇兩種方式訪問底層系統,由程式員選擇:(1).通過java程式調用javaapi調用本地方法,訪問底層系統,與平台無關。(2).通過java程式直接調用本地方法,訪問底層系統與平台相關。本地方法即作業系統提供的方法。
3.類裝載器:裝載java編譯器編譯好的位元組碼*.class和java api的位元組碼到方法區。
java有兩種類裝載器:(1).啟動類裝載器:系統唯一,屬於虛擬機器的一部分,用特定語言編寫(與虛擬機器體層語言相通)使用預設裝載類,主要用來裝載核心類庫
。(2).使用者自訂類裝載器:可有任意多個,用java編寫,屬於java應用程式的一部分,能被編譯成位元組碼,並被虛擬機器所裝載。
一個裝載器裝載一個類及其該類所調用的一切類,使他們相互聯絡,並形成一個命名空間(name space),每一個類裝載器對應一個命名空間。
即java中名字空間的原理。
類裝載器成線形排列,自底向上,頂部為啟動類裝載器。除啟動類裝載器外,其他類裝載器都由使用者執行個體化,用來裝載不同的類。當要裝載一
個類時,底部的裝載器試圖將該類交給父裝載器裝載,而該父類又試圖交給他的父類裝載,一直向上,直到啟動類裝載器。若父類裝載器無法
裝載,則交給子類裝載器裝載,子類裝載能裝載的部分,將餘下部分交給他的子類,直到底部。如:裝載器a,b,c,d,e,f,啟動a--->b--->c--->d--->e--->f--->啟動當有一個類fun需要被裝載時,他會一直上溯到頂部即啟動類裝載器。如果啟動類裝載器無法裝載fun則交給f裝載,f裝載能裝載的部分,將其
餘部分交給e,然後一直這樣下去。
如上所述,運行過程中每個類裝載器裝載的類形成一個運行時包,同一運行時包裡的類可以互相訪問,但不能訪問包外部的類。
4.虛擬機器的生命週期:每個java程式都有自己的虛擬機器執行個體,隨著程式的產生和消亡而產生與消亡。
5.java程式運行時的記憶體結構:程式空間分為方法區,堆,java棧,本地方法棧。
(1)方法區存放裝載的類資料資訊包括:基本資料:每個類的全限定名。每個類的直接超類的全限定名(可約束類型轉換)。該類是類還是介面。該類型的存取修飾詞。直接超介面的全限定名的有序列表。
每個已裝載類的詳細資料:運行時常量池:存放該類型所用的一切常量(直接常量和對其他類型,欄位,方法的符號引用),它們以數組形式通過索引被訪問,是外部調用
與類聯絡及類型對象化的橋樑。它是類檔案(位元組碼)常量池的運行時表示。(還有一種靜態常量池,在位元組碼檔案中)。欄位資訊:類中聲明的每一個欄位的資訊(名,類型,修飾符)。方法資訊:類中聲明的每一個方法的資訊(名,放回類型,參數類型,修飾符,方法的位元組碼和異常表)。靜態變數到類classloader的引用:即到該類的類裝載器的引用。到類class的引用:虛擬機器為每一個被裝載的類型建立一個class執行個體,用來代表這個被裝載的類。
(2)堆存放所有產生的對象及對象的執行個體變數。
(3)java棧以幀的形似存放本地方法的調用狀態(包括方法調用的參數,局部變數,中間結果等)。每調用一個方法就將對應該方法的方法幀壓入
java棧,成為當前方法幀。當調用結束(返回)時,就彈出該幀。編譯器將原代碼編譯成位元組碼(.class)時,就已經將各種類型的方法的局部變
量,運算元棧大小確定並放在位元組碼中,隨著類一併裝載入方法區。當調用方法時,通過存取方法區中的類的資訊,得到局部變數以及運算元
棧的大小。
java棧幀(即方法幀)由局部變數區,運算元棧,幀資料區組成。
局部變數區為一個以字為單位的數組,每個數組元素對應一個局部變數的值。調用方法時,將方法的局部變數組成一個數組,通過索引來訪問
。若為非靜態方法,則加入一個隱含的引用參數this,該參數指向調用這個方法的對象。而靜態方法則沒有this參數。因此,對象無法調用靜態
方法。
運算元棧也是一個數組,但卻是通過棧操作來訪問。所謂運算元是那些被指令操作的資料。當需要對參數操作時如a=b+c,就將即將被操作的參
數壓棧,如將b和c壓棧,然後由操作指令將他們彈出,並執行操作,此處由iadd指令將b和c彈出並相加,然後壓入運算元棧(一系列均由iadd執
行)然後由i_storex指令將結果彈出,存到索引x指向的局部變數區數組內(此處索引x指向局部變數a)。虛擬機器將運算元棧作為工作區。
幀資料區處理常量池解析,異常處理等。
(4)本地方法棧:與調用的本地方法的語言相關,如調用的是一個c語言方法則為一個c棧。本地方法可以回調java方法。若有java方法調用本地方法,虛擬機器就運行這個本地方法。在虛擬機器看來運行這個本地方法就是執行這個java方法,如果本地方法拋出異常,
虛擬機器就認為是這個java方法拋出異常。
(5)執行程式時,通過對象的引用在方法區中尋找裝載的類,若還沒有裝載,則尋找位元組碼(類名.class),並將其裝載入方法區。在執行過程中,虛擬機器會將對象的符號引用(即對象名)替換為直接的指標,以提高訪問速度。
(6)因此,大體可以表述為:方法區:儲存類包括介面的各種資訊,位元組碼裝載到此處。java棧:儲存被調用的方法的各種資訊,只有調用該方法時,才會將該方法幀壓入java棧。堆:儲存物件的資訊,包括對象的執行個體變數,但不包括對象的方法。只有調用對象的方法時,才將方法幀壓入java棧中。
6.java資料類型:數實值型別:浮點類型:float double整數類型:byte,short,int,long,char(int和char可以互換)。參考型別:類類型,介面類型,數群組類型。
7.java的參考型別:引用與指標。引用代表被引用的對象,它只是引用對象的代表,並不佔用記憶體,也不能修改。如引用變數沒有引用對象,則該引用變數=null。指標存放對象的地址,它是一個變數,可以被修改,和其他變數一樣,佔用記憶體。
8.方法區所有線程共用方法區,但為滿足安全執行緒,方法區中每一個類必須被設定為臨界資源,即同一時刻某一個類只能被一個線程訪問。
9.類標識:由於一個程式可以多次裝載同一個類且該類可以存在於不同的名字空間中(即可由不同的裝載器裝載),因此必須將裝載該類的裝載器的標識加
上,才能唯一標識一個類。
10.對象對象執行個體變數儲存在堆中,對象符號引用則在常量池,方法屬性工作表等可能出現的地方。通過對象的引用可以訪問對象的執行個體資料和建立該對象
的類的資料。對象的引用指向堆中的對象。執行個體結構有兩種,見書本98頁。
當調用對象的方法時,需要進行動態綁定。即,不能根據對象來確定需要調用的方法,而是根據對象的類資料來確定需要調用的方法。此時,
也需要通過對象的引用來訪問類資料。動態綁定就是在運行時才綁定,而不是在編譯時間綁定。
11.數組數組也是類的對象。具有相同類型和維數的數組屬於同一個類(不管長度只看維數)。數組的長度屬於對象執行個體。多維陣列也是一維數組。如二
維數組,即為一個一維數組,該一維數組的每個元素是一個數組的引用。數組和普通對象一樣也儲存在堆中。數組名為數組的引用,通過索引即數組標號來訪問數組內容。
12.異常在java棧幀的幀資料區內儲存有針對該方法的異常表的引用。異常表記載了該方法的位元組碼(*.class)受catch子句保護的範圍(即try子句裡的
位元組碼)。當某個方法拋出異常時,虛擬機器在對應的異常表中尋找匹配的catch子句,並將控制權交給catch子句中的代碼。
13.java執行引擎實現平台無關性,以java方法幀裡的運算元棧為中心,將局部變數數組當作cpu的寄存器。每操作一個資料都要壓人運算元棧,然後返回至局部
變數區。java虛擬機器規定強型別轉換,即低精度可以隱式轉換到高精度,高精度必須強制轉換到低精度。
14.線程線程即存在於進程中的某個執行體。每個線程必須遵守對象鎖定,線程等待和通知。對象鎖定使線程互斥的訪問對象資源。等待和通知則是遵守線程合理調度以達到同一個目的。java對象通過指令集達到上鎖目的,同過繼承
object類的wait(),notify(),notifyall()方法來等待和通知。當某個線程調用某個對象的wait()方法時,該線程被阻塞,並加入到該對象的線
程阻塞隊列中,直到另一個線程調用同一對象的通知方法,才能喚醒阻塞隊列中的線程。
15.常量池常量池用來存放類型的各種資訊,包括類型的各種直接常量,和對其他類型,欄位,方法的符號引用。常量池分為兩種,儲存在.class位元組碼中的常量池和儲存在方法區中的運行時常量池。常量池以入口形式(類似於中斷向量表)出現,每個入口都指向一個表,表中儲存常量的資訊。但從常量池的入口的標誌位就可以判斷對應的表
中儲存的常量類型。常量池入口以一個標誌位開始,該標誌位指示該常量的類型。每個入口對應一個表,該表以符號_info結尾,表中存放常量的壓縮形式。常量池除了存放直接常量外還容納如下幾種符號引用:類和介面的全限定名。欄位名稱和描述符(該描述符是一個指示欄位類型的字串。欄位是一個類或介面的類變數或執行個體變數)。方法名稱和描述符(該描述符指示方法傳回型別,參數類型,數量,順序)。
運行時,虛擬機器用常量池的全限定名和方法,欄位的描述符來建立類與類的關係。
常量池僅僅是一個引用和描述符的集合,並不接受任何賦值操作。
所有對象的建立,方法和類變數的調用均要從常量池中擷取資訊,但執行個體變數的調用從堆裡獲得。(猜想)
符號引用是由虛擬機器解析後得到具體的地址來使用。
常量池解析就是將常量池中的符號引用替換成直接引用。
當要使用某個類的方法或欄位時,首先從常量池中找到該方法或欄位的符號引用,然後進行解析,找到其物理地址。
把代碼中出現的各種符號引用,類與類的聯絡,進行常量池解析,叫做動態串連。
16.常量池結構常量池由很多狠多的單元組成,每一個單元都形如(入口|內容),訪問常量池單元時通過索引找到入口,然後訪問其內容。但有時單元的內容也
可能是一個常量池入口(比如類或介面的常量池單元,入口包含該類的符號引用即constant_class_info,而內容則指向一個
constant_utf8_info的常量池單元,該單元裡存放了該類的全限定名)。而直接常量如int,float等,內容處就是常量的值。
17.方法區的結構方法區儲存所有關於類型,介面的資訊。方法區包含:
常量池:儲存類型的直接常量和所有的欄位,方法,其他類型的符號引用(僅僅是引用,並不存放具體資訊)。
欄位資訊:所有聲明的欄位(包括欄位名,類型,修飾符)。
方法資訊:所有定義的方法(包括方法名,傳回型別,修飾符,方法的位元組碼,方法棧幀的大小,方法的異常)。
類變數資訊:虛擬機器在方法區中為所有類變數分配空間,以後的初始化,賦值等操作也在方法區中進行,以便為所有類執行個體共用。
為提高訪問速度,虛擬機器在方法區中為每個非抽象類別設定了一個方法表,該表是一個數組,每個元素是一個方法的直接引用。當類的對象調用
方法時,就在方法表中搜尋(抽象類別沒有執行個體,所以不用調用方法,所以沒有方法表)。
18.堆堆存放類的執行個體和數組(包括執行個體變數,指向對應方法區中類資料的引用)。
19.一個例子class test{public static void main(string args[]){string a=new string("hello");string b=new string("hello");string c="hello";string d="hello";}}則a==b返回false,c==d返回ture。因為:==比較雙方是否是同一個對象。
首先:string a=new string("hello")string b=new string("hello")a和b分別各自建立了hello的對象和引用變數,即在堆中有兩個hello,他們各自的引用是a和b。
而:string c="hello"string d="hello"先建立一個字串類執行個體hello,再建立兩個字串引用變數c和d,然後讓c和d都指向開始建立的hello執行個體。因此c和d指向的是同一個對象。