1. 程式人生 > >java語言與jvm虛擬機簡介

java語言與jvm虛擬機簡介

多態 成員 它的 共享 回收 for 並發 註解 申請

一、java語言

1.1 支持面向對象編程oop

強調支持,因為java同樣可以面向過程編程。

oop的三大特性是:封裝、繼承、多態。

封裝主要針對成員變量而言,oop的思想要求成員變量均為私有,不應該對外能夠訪問,一個符合oop思想的類應該只有公共方法對外能夠訪問;

繼承,主要理解繼承體系,private、protected、public在繼承中的使用場景。理解java是單繼承多實現的(與C++的區別);

多態主要指一個類的實例是運行時決定的,而不是聲明時決定的。父類 a = new 子類();是可以的。這種作用在於可以面向抽象編程、面向接口編程,對象不必必須和聲明的類一致,只要是它的子類、孫子類等即可;

1.2 jdk版本對java語言的改進

1996年發布jdk1.0,java語言具備基礎的oop語法;

1997年發布jdk1.1,引入內部類;

2004年發布jdk1.5,引入語法糖,自動拆裝箱、泛型、動態註解、枚舉、可變長參數、遍歷循環(foreach);

2014年發布jdk8,引入Lambda表達式;

二、jvm虛擬機(本文特指官方默認的HotSpot虛擬機)

2.1 發展

由一家小公司Longview Technologies開發出來,1997年被sun公司收購,jdk1.3之後正式成為官方默認虛擬機;

HotSpot得名來自於其熱點代碼探測技術,可以有效的把熱點代碼探測出來,並利用JIT編譯器將熱點代碼進一步優化並編譯成機器代碼,提高運行效率;

2.2 jvm虛擬機的內存區域組成

程序計數器,線程私有,指向字節碼指令;

java虛擬機棧,線程私有,主要就是描述java方法的,配合程序計數器一起一步一步往下執行方法(理解為什麽是棧);

本地方法棧,線程私有,跟java虛擬機棧類似,區別是它用來執行非java方法;

java堆,線程共享,這是最大的一塊虛擬機內存區域,主要就是我們new的對象都會分配在這裏,這裏分為新生代(Eden、Survivor1、Survivor2)和老年代;

方法區,線程共享,在HotSpot裏叫永久代(Permanent Generation),存放加載的類信息、常量、靜態變量等,static代碼塊、static變量、static方法都會存放在這裏有一個副本。為什麽叫永久代,主要是對這部分的對象實例回收的效率不高,這部分對象實例存活率較高;

運行時常量池,是方法區的一部分;

直接內存,不是虛擬機內存的一部分,指申請虛擬機內存外的內存。

2.3 垃圾收集算法

怎麽樣判斷對象可以回收?有引用計數算法和可達性分析算法。引用計數算法很簡單,給每個對象一個引用計數器,每當有一個地方引用了它,那麽就給它計數器+1,當這個引用失效之後計數器-1,這樣做非常高效,但有一個缺陷是互相引用的對象,無法被回收,造成內存泄露。HotSpot使用可達性分析算法,可達性分析算法從GC Roots對象是否可達來判斷對象是否可以回收,GC Roots對象包括虛擬機棧引用的對象、方法區類靜態屬性引用的對象、方法區常量引用的對象、本地方法棧中引用的對象;

IBM研究指出98%的對象都是朝生夕死,故新生代中回收頻率要較高,每次可以回收大量內存,老年代中經過兩次以上的回收仍存活,說明回收的效率不高,回收頻率可以低一點。另外,大對象不在新生代中分配,而是直接進入老年代。

①標記-清除算法。算法的思想是首先把需要回收的對象標註出來,然後統一清除回收。實現起來很簡單,但標記和清除的效率不高,還會產生大量不連續的內存空間,影響後續為新對象分配內存,尤其是大對象。

②復制算法。針對標記-清除算法的問題,復制算法的思想是把內存區域均等的分成兩塊,比如10M的內存均等分為兩塊5M,每個時刻只能使用一塊,先從第一塊標記仍存活對象的對象,然後統一復制到第二塊內存中,並按內存空間順序排好,第一塊內存則全部回收。第二次回收時就先從第二塊開始,循環往復。這樣做效率很高,並且內存空間可以連續分配。但造成一個問題是本來10M的內存只能用一半,造成內存的浪費。

HotSpot在實際實現復制算法時,將內存空間劃分為Eden和兩個Survivor,且默認Eden和Survivor的比例是8:1:1,新對象分配在Eden中,第一次回收後存活對象被復制到Survivor 1中,Eden全部清除,第二次新對象仍分配在Eden中,第二次回收時Eden和Survivor 1中存活對象被復制到Survivor 2中,Eden和Survivor 1全部清除。

這裏有一個問題:如果存活對象超過內存的10%怎麽辦?這時候就需要從老年代中進行分配擔保(Handle Promotion)。

③標記-整理算法。和新生代不同,在老年代中GC回收的效率不會太高,使用復制算法Survivor空間很可能是不夠的,如果將Survivor調大又浪費內存空間,這時就提出了標記-整理算法應對老年代的實際情況。標記-整理算法內存回收時先將所有存活對象標記出來,但不進行清除,而是將存活對象都往內存的一端移動,那麽內存末端都是可回收的對象,當這些可回收對象被“擠出”內存邊界的時候,則被清除了。

由於HotSpot中把java堆中分為新生代、老年代,他們存活的幾率不一樣,所以按新生代和老年代采取不同的算法,這就叫分代收集算法,在新生代中采取復制算法,在老年代中使用標記-清除或標記-整理算法。

2.4 垃圾收集器

上面分析了新生代、老年代應該采取怎樣的算法,HotSpot中針對實際應用場景,實現了不同的垃圾收集器;

①Serial收集器,復制算法,單線程,Client模式下默認新生代收集器。會有Stop The World問題;

②ParNew收集器,復制算法,多線程,新生代收集器;

③Parallel Scavenge收集器,復制算法,多線程,新生代收集器,與ParNew的區別在於它針對吞吐量設計的;

④Serial Old收集器,標記-整理算法,單線程,Client模式下默認老年代收集器;

⑤CMS收集器,標記-清除算法,多線程,老年代收集器,以降低停頓時間為目標,只在初始標記、重新標記的時候需要Stop The World,采取並發標記和並發清除降低停頓時間;

⑥G1收集器,標記-整理算法+復制算法,多線程,老年代收集器;

三、編譯與運行

3.1 javac編譯器編譯

第一次編譯,將.java文件編譯成中間語言,輸出.Class文件,這期間主要完成語法分析和詞法分析(編譯原理)、註解處理、語義分析(解語法糖等)、生成字節碼Class文件;

3.2 解釋器

類加載進解釋器運行,類加載的過程有:加載、驗證、準備、解析、初始化;

3.3 JIT即時編譯器

解釋器監控熱點代碼為JIT編譯器進一步編譯提供監控數據,觸發JIT編譯器將熱點代碼編譯成機器代碼;

本人知識水平有限,文章難免有紕漏之處,請不吝糾正勘誤。

java語言與jvm虛擬機簡介