1.編譯順序: 編譯器 虛擬機(jī) 虛擬機(jī) java源文件*.java------->字節(jié)碼*.class------>類裝載器--->執(zhí)行引擎
一個.class文件只能包含一個類或接口。因此.java文件中定義了多少類,編譯時就會生成多少.class文件(內(nèi)部類不算)。
2.java程序可以選擇兩種方式訪問底層系統(tǒng),由程序員選擇:(1).通過java程序調(diào)用javaapi調(diào)用本地方法,訪問底層系統(tǒng),與平臺無關(guān)。(2).通過java程序直接調(diào)用本地方法,訪問底層系統(tǒng)與平臺相關(guān)。本地方法即操作系統(tǒng)提供的方法。
3.類裝載器:裝載java編譯器編譯好的字節(jié)碼*.class和java api的字節(jié)碼到方法區(qū)。
java有兩種類裝載器:(1).啟動類裝載器:系統(tǒng)唯一,屬于虛擬機(jī)的一部分,用特定語言編寫(與虛擬機(jī)體層語言相通)使用默認(rèn)方式裝載類,主要用來裝載核心類庫
。(2).用戶自定義類裝載器:可有任意多個,用java編寫,屬于java應(yīng)用程序的一部分,能被編譯成字節(jié)碼,并被虛擬機(jī)所裝載。
一個裝載器裝載一個類及其該類所調(diào)用的一切類,使他們相互聯(lián)系,并形成一個命名空間(name space),每一個類裝載器對應(yīng)一個命名空間。
即java中名字空間的原理。
類裝載器成線形排列,自底向上,頂部為啟動類裝載器。除啟動類裝載器外,其他類裝載器都由用戶實例化,用來裝載不同的類。當(dāng)要裝載一
個類時,底部的裝載器試圖將該類交給父裝載器裝載,而該父類又試圖交給他的父類裝載,一直向上,直到啟動類裝載器。若父類裝載器無法
裝載,則交給子類裝載器裝載,子類裝載能裝載的部分,將余下部分交給他的子類,直到底部。如:裝載器a,b,c,d,e,f,啟動a--->b--->c--->d--->e--->f--->啟動當(dāng)有一個類fun需要被裝載時,他會一直上溯到頂部即啟動類裝載器。如果啟動類裝載器無法裝載fun則交給f裝載,f裝載能裝載的部分,將其
余部分交給e,然后一直這樣下去。
如上所述,運(yùn)行過程中每個類裝載器裝載的類形成一個運(yùn)行時包,同一運(yùn)行時包里的類可以互相訪問,但不能訪問包外部的類。
4.虛擬機(jī)的生命周期:每個java程序都有自己的虛擬機(jī)實例,隨著程序的產(chǎn)生和消亡而產(chǎn)生與消亡。
5.java程序運(yùn)行時的內(nèi)存結(jié)構(gòu):程序空間分為方法區(qū),堆,java棧,本地方法棧。
(1)方法區(qū)存放裝載的類數(shù)據(jù)信息包括:基本信息:每個類的全限定名。每個類的直接超類的全限定名(可約束類型轉(zhuǎn)換)。該類是類還是接口。該類型的訪問修飾符。直接超接口的全限定名的有序列表。
每個已裝載類的詳細(xì)信息:運(yùn)行時常量池:存放該類型所用的一切常量(直接常量和對其他類型,字段,方法的符號引用),它們以數(shù)組形式通過索引被訪問,是外部調(diào)用
與類聯(lián)系及類型對象化的橋梁。它是類文件(字節(jié)碼)常量池的運(yùn)行時表示。(還有一種靜態(tài)常量池,在字節(jié)碼文件中)。字段信息:類中聲明的每一個字段的信息(名,類型,修飾符)。方法信息:類中聲明的每一個方法的信息(名,放回類型,參數(shù)類型,修飾符,方法的字節(jié)碼和異常表)。靜態(tài)變量到類classloader的引用:即到該類的類裝載器的引用。到類class的引用:虛擬機(jī)為每一個被裝載的類型創(chuàng)建一個class實例,用來代表這個被裝載的類。
(2)堆存放所有生成的對象及對象的實例變量。
(3)java棧以幀的形似存放本地方法的調(diào)用狀態(tài)(包括方法調(diào)用的參數(shù),局部變量,中間結(jié)果等)。每調(diào)用一個方法就將對應(yīng)該方法的方法幀壓入
java棧,成為當(dāng)前方法幀。當(dāng)調(diào)用結(jié)束(返回)時,就彈出該幀。編譯器將原代碼編譯成字節(jié)碼(.class)時,就已經(jīng)將各種類型的方法的局部變
量,操作數(shù)棧大小確定并放在字節(jié)碼中,隨著類一并裝載入方法區(qū)。當(dāng)調(diào)用方法時,通過訪問方法區(qū)中的類的信息,得到局部變量以及操作數(shù)
棧的大小。
java棧幀(即方法幀)由局部變量區(qū),操作數(shù)棧,幀數(shù)據(jù)區(qū)組成。
局部變量區(qū)為一個以字為單位的數(shù)組,每個數(shù)組元素對應(yīng)一個局部變量的值。調(diào)用方法時,將方法的局部變量組成一個數(shù)組,通過索引來訪問
。若為非靜態(tài)方法,則加入一個隱含的引用參數(shù)this,該參數(shù)指向調(diào)用這個方法的對象。而靜態(tài)方法則沒有this參數(shù)。因此,對象無法調(diào)用靜態(tài)
方法。
操作數(shù)棧也是一個數(shù)組,但卻是通過棧操作來訪問。所謂操作數(shù)是那些被指令操作的數(shù)據(jù)。當(dāng)需要對參數(shù)操作時如a=b+c,就將即將被操作的參
數(shù)壓棧,如將b和c壓棧,然后由操作指令將他們彈出,并執(zhí)行操作,此處由iadd指令將b和c彈出并相加,然后壓入操作數(shù)棧(一系列均由iadd執(zhí)
行)然后由i_storex指令將結(jié)果彈出,存到索引x指向的局部變量區(qū)數(shù)組內(nèi)(此處索引x指向局部變量a)。虛擬機(jī)將操作數(shù)棧作為工作區(qū)。
幀數(shù)據(jù)區(qū)處理常量池解析,異常處理等。
(4)本地方法棧:與調(diào)用的本地方法的語言相關(guān),如調(diào)用的是一個c語言方法則為一個c棧。本地方法可以回調(diào)java方法。若有java方法調(diào)用本地方法,虛擬機(jī)就運(yùn)行這個本地方法。在虛擬機(jī)看來運(yùn)行這個本地方法就是執(zhí)行這個java方法,如果本地方法拋出異常,
虛擬機(jī)就認(rèn)為是這個java方法拋出異常。
(5)執(zhí)行程序時,通過對象的引用在方法區(qū)中查找裝載的類,若還沒有裝載,則查找字節(jié)碼(類名.class),并將其裝載入方法區(qū)。在執(zhí)行過程中,虛擬機(jī)會將對象的符號引用(即對象名)替換為直接的指針,以提高訪問速度。
(6)因此,大體可以表述為:方法區(qū):存儲類包括接口的各種信息,字節(jié)碼裝載到此處。java棧:存儲被調(diào)用的方法的各種信息,只有調(diào)用該方法時,才會將該方法幀壓入java棧。堆:存儲對象的信息,包括對象的實例變量,但不包括對象的方法。只有調(diào)用對象的方法時,才將方法幀壓入java棧中。
6.java數(shù)據(jù)類型:數(shù)值類型:浮點類型:float double整數(shù)類型:byte,short,int,long,char(int和char可以互換)。引用類型:類類型,接口類型,數(shù)組類型。
7.java的引用類型:引用與指針。引用代表被引用的對象,它只是引用對象的代表,并不占用內(nèi)存,也不能修改。如引用變量沒有引用對象,則該引用變量=null。指針存放對象的地址,它是一個變量,可以被修改,和其他變量一樣,占用內(nèi)存。
8.方法區(qū)所有線程共享方法區(qū),但為滿足線程安全,方法區(qū)中每一個類必須被設(shè)定為臨界資源,即同一時刻某一個類只能被一個線程訪問。
9.類標(biāo)識:由于一個程序可以多次裝載同一個類且該類可以存在于不同的名字空間中(即可由不同的裝載器裝載),因此必須將裝載該類的裝載器的標(biāo)識加
上,才能唯一標(biāo)識一個類。
10.對象對象實例變量存儲在堆中,對象符號引用則在常量池,方法屬性表等可能出現(xiàn)的地方。通過對象的引用可以訪問對象的實例數(shù)據(jù)和創(chuàng)建該對象
的類的數(shù)據(jù)。對象的引用指向堆中的對象。實例結(jié)構(gòu)有兩種,見書本98頁。
當(dāng)調(diào)用對象的方法時,需要進(jìn)行動態(tài)綁定。即,不能根據(jù)對象來確定需要調(diào)用的方法,而是根據(jù)對象的類數(shù)據(jù)來確定需要調(diào)用的方法。此時,
也需要通過對象的引用來訪問類數(shù)據(jù)。動態(tài)綁定就是在運(yùn)行時才綁定,而不是在編譯時綁定。
11.數(shù)組數(shù)組也是類的對象。具有相同類型和維數(shù)的數(shù)組屬于同一個類(不管長度只看維數(shù))。數(shù)組的長度屬于對象實例。多維數(shù)組也是一維數(shù)組。如二
維數(shù)組,即為一個一維數(shù)組,該一維數(shù)組的每個元素是一個數(shù)組的引用。數(shù)組和普通對象一樣也存儲在堆中。數(shù)組名為數(shù)組的引用,通過索引即數(shù)組標(biāo)號來訪問數(shù)組內(nèi)容。
12.異常在java棧幀的幀數(shù)據(jù)區(qū)內(nèi)保存有針對該方法的異常表的引用。異常表記載了該方法的字節(jié)碼(*.class)受catch子句保護(hù)的范圍(即try子句里的
字節(jié)碼)。當(dāng)某個方法拋出異常時,虛擬機(jī)在對應(yīng)的異常表中尋找匹配的catch子句,并將控制權(quán)交給catch子句中的代碼。
13.java執(zhí)行引擎實現(xiàn)平臺無關(guān)性,以java方法幀里的操作數(shù)棧為中心,將局部變量數(shù)組當(dāng)作cpu的寄存器。每操作一個數(shù)據(jù)都要壓人操作數(shù)棧,然后返回至局部
變量區(qū)。java虛擬機(jī)規(guī)定強(qiáng)類型轉(zhuǎn)換,即低精度可以隱式轉(zhuǎn)換到高精度,高精度必須強(qiáng)制轉(zhuǎn)換到低精度。
14.線程線程即存在于進(jìn)程中的某個執(zhí)行體。每個線程必須遵守對象鎖定,線程等待和通知。對象鎖定使線程互斥的訪問對象資源。等待和通知則是遵守線程合理調(diào)度以達(dá)到同一個目的。java對象通過指令集達(dá)到上鎖目的,同過繼承
object類的wait(),notify(),notifyall()方法來等待和通知。當(dāng)某個線程調(diào)用某個對象的wait()方法時,該線程被阻塞,并加入到該對象的線
程阻塞隊列中,直到另一個線程調(diào)用同一對象的通知方法,才能喚醒阻塞隊列中的線程。
15.常量池常量池用來存放類型的各種信息,包括類型的各種直接常量,和對其他類型,字段,方法的符號引用。常量池分為兩種,存儲在.class字節(jié)碼中的常量池和存儲在方法區(qū)中的運(yùn)行時常量池。常量池以入口形式(類似于中斷向量表)出現(xiàn),每個入口都指向一個表,表中存儲常量的信息。但從常量池的入口的標(biāo)志位就可以判斷對應(yīng)的表
中存儲的常量類型。常量池入口以一個標(biāo)志位開始,該標(biāo)志位指示該常量的類型。每個入口對應(yīng)一個表,該表以符號_info結(jié)尾,表中存放常量的壓縮形式。常量池除了存放直接常量外還容納如下幾種符號引用:類和接口的全限定名。字段名稱和描述符(該描述符是一個指示字段類型的字符串。字段是一個類或接口的類變量或?qū)嵗兞?。方法名稱和描述符(該描述符指示方法返回類型,參數(shù)類型,數(shù)量,順序)。
運(yùn)行時,虛擬機(jī)用常量池的全限定名和方法,字段的描述符來建立類與類的關(guān)系。
常量池僅僅是一個引用和描述符的集合,并不接受任何賦值操作。
所有對象的創(chuàng)建,方法和類變量的調(diào)用均要從常量池中獲取信息,但實例變量的調(diào)用從堆里獲得。(猜想)
符號引用是由虛擬機(jī)解析后得到具體的地址來使用。
常量池解析就是將常量池中的符號引用替換成直接引用。
當(dāng)要使用某個類的方法或字段時,首先從常量池中找到該方法或字段的符號引用,然后進(jìn)行解析,找到其物理地址。
把代碼中出現(xiàn)的各種符號引用,類與類的聯(lián)系,進(jìn)行常量池解析,叫做動態(tài)連接。
16.常量池結(jié)構(gòu)常量池由很多狠多的單元組成,每一個單元都形如(入口|內(nèi)容),訪問常量池單元時通過索引找到入口,然后訪問其內(nèi)容。但有時單元的內(nèi)容也
可能是一個常量池入口(比如類或接口的常量池單元,入口包含該類的符號引用即constant_class_info,而內(nèi)容則指向一個
constant_utf8_info的常量池單元,該單元里存放了該類的全限定名)。而直接常量如int,float等,內(nèi)容處就是常量的值。
17.方法區(qū)的結(jié)構(gòu)方法區(qū)存儲所有關(guān)于類型,接口的信息。方法區(qū)包含:
常量池:存儲類型的直接常量和所有的字段,方法,其他類型的符號引用(僅僅是引用,并不存放具體信息)。
字段信息:所有聲明的字段(包括字段名,類型,修飾符)。
方法信息:所有定義的方法(包括方法名,返回類型,修飾符,方法的字節(jié)碼,方法棧幀的大小,方法的異常)。
類變量信息:虛擬機(jī)在方法區(qū)中為所有類變量分配空間,以后的初始化,賦值等操作也在方法區(qū)中進(jìn)行,以便為所有類實例共享。
為提高訪問速度,虛擬機(jī)在方法區(qū)中為每個非抽象類設(shè)置了一個方法表,該表是一個數(shù)組,每個元素是一個方法的直接引用。當(dāng)類的對象調(diào)用
方法時,就在方法表中搜索(抽象類沒有實例,所以不用調(diào)用方法,所以沒有方法表)。
18.堆堆存放類的實例和數(shù)組(包括實例變量,指向?qū)?yīng)方法區(qū)中類數(shù)據(jù)的引用)。
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指向的是同一個對象。
本文出自 51CTO.COM技術(shù)博客