譯文地址:https://anturis.com/blog/java-virtual-machine-the-essential-guide/java
介紹:編程
Java虛擬機(JVM)是Java應用程序的執行環境。在通常的意義上來講,JVM是一種抽象的計算機,一般,Java虛擬機是指用嚴格的指令集以及全面的內存模型來具體實施本規範的。它也能夠指的是軟件實現的運行時實例。主要的參考實現的JVM是HotSpot。
緩存
JVM規範確保任何實現可以以徹底相同的方式來解釋字節碼。它能夠被實現爲一個過程,一個獨立的Java操做系統,或者直接執行字節碼的處理器芯片。最廣爲人知的JVM是做爲流行的平臺(Windows,Mac OS X,Linux上,Solaris等)的進程中運行的軟件實現。 服務器
JVM的架構可以詳細控制Java應用程序執行的操做。它運行在沙箱環境,並確保應用程序沒有訪問本地文件系統,進程和網絡沒有適當的權限。若是遠程執行的,代碼應該使用證書進行簽名。網絡
除了解釋Java字節碼,JVM的大多數軟件實現包括剛剛在實時(JIT)編譯器生成機器代碼爲常用的方法。機代碼是CPU的本機語言,而且能夠執行比解釋字節碼快得多。
你並不須要瞭解Java虛擬機是如何工做的發展和運行Java應用程序。可是,若是你對多性能問題有必定的瞭解,你將避免不少問題。架構
架構:併發
JVM規範定義了子系統和它們的外在行爲。 Java虛擬機具備如下主要的子系統:編程語言
Class Loader:負責讀取Java源代碼和加載類到數據區域。ide
Execution Engine:負責從數據區域執行指令。性能
數據區佔據了由JVM從底層操做系統分配的內存。
JVM使用分爲如下幾個層次不一樣的類加載器:
(1)引導類加載器是父加載器爲其餘類加載器。它加載了核心Java庫,而且是惟一一個用本機代碼。
(2)擴展類加載器是引導類加載器的一個孩子。它加載的擴展庫。
(3)系統類加載器是擴展類加載器的一個孩子。它加載被髮如今classpath中,是應用程序的類文件。
(4)用戶定義的類加載器是系統類加載器或其它用戶定義的類加載器的一個孩子。
當一個類加載器接收到一個請求加載一個類時,它會檢查緩存,看看這個類已經被加載,而後委託請求父。若是父沒法加載的類,那麼孩子嘗試加載類自己。子類加載器能夠檢查父類加載器的緩存,但父不能看到孩子裝入的類。這種設計,由於一個孩子的類加載器不該該被容許加載已裝入由它的父類。
執行引擎從裝載到數據區域逐個字節碼執行命令。使字節碼指令變成可讀的機器碼,所述的執行引擎使用方法有兩種
Interpretation:當遇到它時,執行引擎會改變每一個命令爲機器語言
Just-in-time (JIT) compilation:若是一個方法被被常用,執行引擎將其編譯爲本機代碼,並將其存儲在緩存中。在此以後,使用該方法相關聯的全部命令都沒有直接解釋執行。
雖然JIT編譯時間比解釋的時間多,它是一種方法,可能會被調用數千次只進行一次。運行這樣的方法做爲本機代碼比每次遇到一個命令節省了大量的時間。
JIT編譯不是JVM規範的要求,它不是用來提升JVM性能的惟一方法。該規範只定義了其中的字節碼指令涉及的本機代碼;它是由實現去定義所述的執行引擎怎樣執行這種轉換。
Java內存模型是創建在自動內存管理的概念。當一個對象再也不被應用程序引用時,垃圾收集器會丟棄它,它會釋放內存。這是與不少其餘編程語言,你必須手動卸載內存中的對象不一樣的。
JVM對底層操做系統分配內存,將其分爲如下幾個方面。
Heap Space:這是用來存放對象的共享內存區域,一個垃圾收集掃描器
Method Area:這個地區之前被稱爲持久代,其中裝載的類分別存放近來已經從JVM和類除去的資源
Native Area:這一領域是基本類型和引用變量
將堆分紅幾代確保高效的內存管理,由於垃圾收集器並不須要掃描整個堆。大多數對象居住在很短的時間,以及那些存活時間較長可能不須要被丟棄直到應用程序終止
當一個Java應用程序建立一個對象,它是存儲在堆中的伊甸園池。一旦滿了,一個小垃圾收集被觸發在伊甸園池。首先,垃圾收集器標記死的對象(那些不被應用程序的任何多個被引用),並遞增活對象的時間(時間是由該對象已存活垃圾回收的數量來表示)而後垃圾收集器會丟棄死的對象和移動活的對象生活的倖存者池,離開伊甸園池。
當一個倖存的對象達到了必定的時間,就被移動到了老一代堆:將終身池。最終,終身池填滿和主要的垃圾收集被觸發把它清理乾淨
當進行垃圾收集,全部應用程序線程被中止,從而致使停頓。小型垃圾回收頻繁,但通過優化,可以迅速去除死的對象,這是年輕一代的重要組成部分。這比主要的垃圾收集慢得多,由於它們涉及到時間長的對象。有各類不一樣的垃圾收集器中,執行的主要垃圾收集時,有些收集器可能會在某些狀況下更快。
堆的大小是動態的。存儲器被分配給僅當它是必需的堆當堆填滿時,JVM從新分配更多的內存,直到達到最大值內存從新分配也致使應用程序簡單地停下來。
在JVM運行在一個單一進程,但它能夠同時執行多個線程,每個都運行其本身的方法這就是Java的一個重要部分如即時通信客戶端的應用程序,運行在至少兩個線程;一個等待用戶輸入,一個用於檢查服務器收到的消息另外一個例子是,在執行不一樣的線程請求的服務器應用程序:有時每一個請求能夠包括同時運行多個線程
全部線程共享內存,並提供給JVM進程的其餘資源。每一個JVM進程開始於入口點的主線程(main()方法)其餘線程是從它開始,目前執行的獨立路徑線程能夠並行地在不一樣的處理器上運行,也能夠共享一個處理器線程調度器控制線程如何執行在單個處理器上。
JVM的性能取決於它是如何以及配置相匹配的應用程序的功能雖然內存使用的垃圾收集和從新分配內存的進程自動管理,但你必須控制他們的頻率,通常狀況下,你的應用內存需求的越多,你內存管理用的就少,這樣就會中止你的應用
若是垃圾收集正在發生的頻率比你想的快,你就可使用更大的堆,它須要整整一代堆的用更長的時間來填補,就會有越少的垃圾收集發生,配置最大堆的大小,使用Xmx 選項,當你開啓JVM默認狀況下,將最大將最大堆大小設置爲可用的物理存儲器的操做系統的任一1/4,或爲1 GB(取最小)
若是問題是從新分配內存,能夠設置初始堆大小是同樣的最大值。這意味着,JVM將永遠須要分配更多的內存堆不過,你也將失去動態的堆大小所得到的自適應內存優化堆將是目前的固定大小的,你啓動應用程序要配置初始堆大小,使用-Xms選項,當您啓動JVM默認狀況下,初始堆尺寸設置爲可用的物理存儲器的操做系統的任一1/64,或一些合理的最低是的不一樣的平臺(取最大)
若是你知道哪些垃圾收集(或大或小),可能會形成性能降低,你能夠設置老小兩代人之間的比例,而不改變總體的堆大小,對於應用那將創造更多的短命的對象,增長年輕一代的大小(那將留下給老對象的內存)對於使用長了很多幸存的對象進行操做的應用程序,增長了老一代的大小(經過爲年輕一代的設置更少的內存)經過如下方法能夠用來控制老小世代的內存大小
(1)當您啓動JVM NewRatio選項:指定老小一代使用-XX之間的比率例如,爲了使老一代比年輕一代的五倍,指定某某:NewRatio=5默認狀況下,該比率設置爲2(老一代佔堆⅔,而年輕一代佔據⅓)
(2)指定年輕一代使用,當你啓動JVM的-Xmn選項的初始和最大大小老一代的尺寸將被設置任何記憶仍保留在堆中
(3)分別指定了年輕一代的初始和最大大小,使用XX:NewSize和某某:當你開始MaxNewSize選項在JVM老一代的尺寸將被設置任何記憶留在堆
大多數應用程序(尤爲是服務器)須要併發執行,處理多項任務有些任務是在給定時刻更重要的,有些則是可每當CPU是否是忙着作別的執行後臺任務任務在不一樣的線程中執行。例如,一臺服務器可能有一個低優先級的線程計算基於一些數據統計和啓動更高優先級的線程來處理傳入的數據,而另外一個高優先級的線程服務請求的一些計算得出的數據。能夠存在的數據從服務器請求數據許多來源,和許多客戶機。每一個請求都將短暫中止的後臺計算線程的執行服務請求因此,你必須監控正在運行的線程數量,並確保有足夠的CPU時間來制定必要的計算線程
每一個線程有一個棧,保存的方法調用,返回地址,等等一些內存分配的堆棧,若是有太多的線程,這可能會致使內存溢出的錯誤。即便你有分配給的對象足夠的堆內存,應用程序可能沒法啓動一個新線程在這種狀況下,考慮限制堆棧的最大大小的線程。要配置的線程堆棧大小,使用-Xss選項,當您啓動JVM默認狀況下,線程堆棧大小設置爲320 KB或1024 KB,這取決於平臺
不管您是開發或正在運行的Java應用程序,它監視JVM的性能是很是重要的配置JVM是否是一次性的事情,尤爲是若是你正在處理的Java上運行的服務器你必須不斷地檢查這兩個堆和非堆內存,該應用程序建立的線程的數目,而且被加載到內存中的類的數量的分配和使用這些核心參數
使用Anturis控制檯,你能夠設置監控JVM的任何硬件組件(如運行的Tomcat Web服務器的計算機)中加入Java虛擬機監視器組件的基礎設施。
Java虛擬機監視器能夠測量如下指標:
(1)總的內存使用量(MB)是在JVM使用的內存量。若是JVM會消耗全部可用內存,該指標會影響底層操做系統的總體性能
(2)堆內存使用量(MB)的內存是在JVM分配的使用正在運行的Java應用程序對象的數量。未使用的對象是在堆由垃圾回收器按期清除若是這個指標增加,它可能代表給你的應用程序是否是移除未使用的的對象引用,或者你須要正確配置垃圾收集器
(3)非堆內存使用量(MB)的內存分配方法區和代碼緩存量。方法區用於存儲引用加載的類。若是這些參考文獻沒有適當除去持久代池,能夠增長每一個應用程序被從新部署的時間,致使了非堆內存泄漏。它也能夠表示一個線程的建立泄漏。
(4)總池內存使用量(MB)是由JVM分配的各類內存池的內存(也就是總的內存,而不代碼緩存區),這能夠給你一個你的應用程序使用內存的狀況
(5)線程(線程)是JVM中的活動線程的數目。舉例來講,每一個請求到Tomcat服務器用一個單獨的線程來處理,因此這個指標能夠給你當前正在處理的請求的數目的概念,以及它是否會影響設置爲線程運行後臺任務較低的優先級。
(6)類(類)加載類的數量。若是您的應用程序動態建立了大量的類,這多是一個嚴重的內存泄漏源
完了,寫到這不知不覺感受本身翻譯的太爛了,你們仍是讀原文吧!