国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

JVM概論

2019-11-14 23:16:45
字體:
來源:轉載
供稿:網友
JVM概論

引子

  java虛擬機是Java應用程序的執行環境。通常而言,JVM是由一組嚴格的指令集和一個復雜的內存模型來具體實現的虛擬機,它用來解釋編譯好的java字節碼文件,將字節碼轉換為特定機器可以執行的本機代碼(native code)。它也可以指代某一軟件運行時的進程實例。這里,我們以hotspot實現的JVM為例。

  JVM的規則保證任何一款具體實現的JVM都要以完全相同的方式去解釋java字節碼文件,無論是一個進程,一個獨立的java操作系統,抑或是一個直接執行字節碼命令的處理器芯片。一般情況下,我們通常討論的JVM是一個運行在操作系統上的進程。

  JVM的架構設計使得它可以精細的控制JAVA應用程序的每一個動作,在沒有權限的情況下,應用程序無法去訪問本地文件系統,處理器,網絡等。例如,在遠程操作的情況下,代碼需要有簽名證書。

  除了去解釋java字節碼,許多軟件實現的JVM都有一個JIT編譯器用于生成頻繁執行的方法機器代碼。機器代碼是可以直接被cpu解析執行的,所以比字節碼速度更快。

你無需去理解JVM的內部,就能編寫并運行一個JAVA應用程序。但是,如果你知道了其中的一些原理,就能避免一些性能上的問題。本文以sunspot為例子來說明。

架構

  JVM主要有兩大子系統:

  • 類加載器:負責讀取java源文件,并把類加載進內存。
  • 執行引擎:負責執行指令。

  這里的內存是底層操作系統分配給JVM的,如下所示:

類加載器

  JVM應用不同類型的類加載器構造了層次結構:

  • bootstrap類加載器,它是其他類加載器的父母,負責加載核心java類庫,并且是唯一的一個使用本機代碼進行編寫的類加載器。
  • 擴展類加載器,它是bootstrap類加載器的孩子,負責加載擴展類庫。
  • 系統類加載器,它是擴展類加載器的的孩子, 負責從classpath中加載類文件。
  • 用戶自定義類加載器,它是系統類加載器或其他用戶自定義類加載器的孩子。

  當類加載器收到去加載一個類的請求時,會去檢查cache中該類是否已經被加載,然后向其父加載器發出加載請求,如果其父加載器加載失敗,那么它就自己進行加載。一個子類加載器可以檢查其父類加載器的cache中是否加載了某個類,但是父類加載器無法查看子類cache中的緩存。這樣設計的原因是為了防止子類加載器加載那些已經被父類加載器加載過的類。(呼,好繞口。。。)

  java文件經過編譯后會生成.class字節碼文件,它定義了JVM中的一個類型,包括域,方法,繼承信息,注解和其他元數據。我們知道,類是JVM能加載的最小程序代碼單元,將一個新的類加入到當前運行中的JVM中,首先要對類文件進行加載和連接,然后將一個代表該類的Class對象交給JVM,才可以創建新的實例。

加載與連接

  JVM要執行.class文件中的字節碼,首先必須以字節流的方式將文件讀入,然后轉化為可以使用的格式加入到運行的JVM中。這兩步被稱為加載與連接。

加載

  這個過程首先會創建一個字節數組,然后從文件系統中讀取構成類文件的字節流,最后產生與所加載類對應的Class對象。這個過程中會對類做一些基本檢查,加載結束后,Class對象還不完整,所以類是不可用的。

連接

  加載工作完成后,類需要被連接起來,這里分為3個階段:

  • 驗證:正式類文件符合預期,不會引起運行時錯誤或其他問題。主要包括完整性檢查,常量池檢查,字節碼檢查。

  • 準備:在類文件中引用的其他類型需要全部定位到,分配內存確保該類準備就緒。此時并不會初始化變量,也不會執行任何字節碼。

  • 解析:解析會促使JVM檢查類文件中引用到的類型是否都是已知類型,如果此時有未被加載進來的類,則會觸發新的加載過程。

  • 初始化:初始化靜態變量,靜態初始化代碼塊運行,類可以使用了。

  連接與加載的最終產物是一個Class對象,它可以表示加載并連接起來的新類型,可以用它來創建新實例。

執行引擎

  執行引擎負責執行被加載進內存的字節碼指令,為了使計算機能夠識別字節碼,執行引擎采用了兩種方式:

  • 解釋:執行引擎將字節碼解釋成機器語言。
  • JIT編譯:如果一個方法被頻繁的執行,執行引擎會把該方法的字節碼編譯成本機代碼存于cache中,于是,所有與該方法相關的指令都無需解釋,直接執行。

  盡管JIT的編譯過程比普通的解釋過程要耗時,但是它只需編譯一次,對于那些上千次調用的方法來說,直接執行機器代碼就比每次都要轉換字節碼再執行要劃算了。

  JIT編譯器對于JVM而言并非是必須的組件,同時,也不是提升JVM性能的唯一手段。JVM規范只是定義了字節碼與機器代碼的對應關系,至于如何具體實現,就是不同版本JVM的事情了。

內存模型

  JAVA內存模型是建立在內存自動管理機制之上的。當一個對象不在被應用程序引用,垃圾收集器GC就丟棄它并釋放內存。這與其他編程語言需要手動釋放對象的方式不同。

  JVM從操作系統中申請來內存,并分割成如下幾個區域:

  • 堆:存放對象實例的共享區,GC會來進行掃描。
  • 方法區:用于存放類加載器加載進來的字節碼,靜態變量,常量等等。最近,它被JVM移除,類被當做元數據加載進本機操作系統的內存。
  • 棧:存放基本類型變量和類對象實例的引用,線程私有。

垃圾回收

  內存自動管理是JAVA平臺最重要的組成部分。一個java進程既有棧又有堆,其中,棧保存了基本類型的局部變量,以及自定義類型變量在堆中存放的地址。堆中保存了要創建的對象。java對堆內存回收和再利用的基本算法被稱為標記和清除。

  最簡單的標記和清除算法首先會暫停所有正在運行的線程,然后堆中遍歷引用樹,標記出“活”的對象,遍歷完成后則清除回收所有未被標記的對象。其中,“活”的對象是指在任意用戶線程的棧幀中存在引用的對象。被清除的內存并不會還給OS,而是交給JVM。

  JAVA對標記清除算法做了改進,采用“分代式垃圾收集”方法,因為對象的生存期或者很短或者很長,所以根據對象的生命周期將堆內存劃分為不同區域,充分利用對象生命周期的特點。因此,同一個對象在其不同生命周期中,對它的引用可能指向了不同的內存區域。

  將堆根據類實例的生存周期劃分為不同區域使得內存管理更加有效,GC無需遍歷整個堆。絕大多數對象的生命周期都很短,而那些略長一些的對象所占內存在程序結束之前不大可能被全部回收。

內存區域劃分

  • Eden區:Eden區中存放剛創建的對象,大多數對象都僅僅存在過這里。
  • Survivor Space:這個區域通常被劃分為兩個區域S0和S1,從Eden中幸存下來未被清除的對象會被挪到其中一個區域中。
  • tenured區:這個區域中存放從Survivor Space區域中挪過來的對象。

收集方式

  對不同區域的內存回收方式是不同的,具體來講主要分為年輕代收集和完全收集?!?/p>

年輕代收集

  我們將Eden區和Survivor Space稱為年輕代,對這部分內存的清理與收集的過程很簡單:

  • 標記:當一個類對象被創建,它會被存于堆的eden池中,當Eden池滿時就會觸發young GC。在遍歷年輕代區域的過程中,將發現的“活”對象都標記出來并挪走。
    1. GC遍歷Eden區和Survivor Space區,標記出“活”對象并增加那些仍然存活的對象的"壽命值"(使用經歷過垃圾回收次數來表示)。然后進行Eden區+有對象的Survivor區(設為S0區)垃圾回收,把存活的對象用復制算法拷貝到一個空的Survivor(S1)中,此時Eden區被清空,另外一個Survivor S0也為空。下次觸發Young GC回收Eden+S1,將存活對象拷貝到S0中。新生代垃圾回收簡單、粗暴、高效。(每次Young GC都會使Survivor區存活對象值+1,直到閾值)。
    2. 將Survivor Space區中S1足夠老(被標記次數足夠多)的對象挪進tenured區。
  • 清除:最后,清空Eden區和S1區就可以重用了。

完全收集

  當tenured區滿了,年輕代收集就無法把對象放入tenured區了,這時候會觸發一次完全收集。根據老年代所用的垃圾收集器,對老年代對象進行內部遷移。

發生一次 Major GC 至少伴隨一次Young GC,一般比JVM在tenured區申請不到內存,會進行Full GC。tenured區使用一般采用Concurrent-Mark–Sweep策略回收內存。

當一個GC運行時,應用程序所有的進程都將停止。Young GC很頻繁,但是會很快清理Eden池中的對象。而Major GC由于涉及到大量仍存活的對象,所以比Young GC慢很多。

堆內存是動態的。當堆內存滿時,JVM會重新分配內存給它直到最大限度,同時也停止應用程序進程來完成內存分配。

線程

  JVM是一個單進程,但是它可以并發多個線程,不同線程執行自己的方法。所有的線程共享著JVM分配到的資源。JVM進程在程序入口(main方法)新開一個線程,其余的線程都來自與此線程,并獨立執行。多個線程可以并發地在不同處理器中執行,或者共享同一個處理器。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 龙江县| 孟连| 天等县| 大方县| 青州市| 大港区| 永安市| 金门县| 涿鹿县| 石林| 常州市| 冷水江市| 绿春县| 化德县| 衡水市| 高邮市| 墨玉县| 江北区| 山阳县| 台北县| 同江市| 朝阳县| 广水市| 揭东县| 册亨县| 屯昌县| 涟水县| 轮台县| 禹城市| 出国| 江油市| 农安县| 塔河县| 鲜城| 德阳市| 南汇区| 苏尼特右旗| 石门县| 腾冲县| 天台县| 崇文区|