編譯型語言:程序在執行以前須要一個專門的編譯過程,把程序編譯成爲機器語言的文件,運行時不須要從新翻譯,直接使用編譯的結果就好了。所以效率比較高。好比 C 語言。java
解釋型語言:程序不須要編譯,程序在運行時才翻譯成機器語言,每執行一次都要翻譯一次。所以效率比較低。好比Basic語言,專門有一個解釋器可以直接執行Basic程序,每一個語句都是執行的時候才翻譯。算法
C語言是編譯型的。C程序——>機器語言(編譯)數組
Java比較特殊,Java程序也須要編譯,可是沒有直接編譯成機器語言,而是編譯成字節碼,而後用解釋方式執行字節碼。 Java程序—— >字節碼(編譯)—— >機器語言(解釋)bash
JVM 主要由 ClassLoader 和 執行引擎 兩子系統組成,運行數據區分爲五個部分: 方法區、堆、棧、程序計數器、本地方法棧。其中的方法區和堆是全部線程共享的,JVM將臨時變量放在棧中,每一個線程都有本身獨立的棧空間和程序計數器。服務器
任何一個Java類的main函數運行都會建立一個JVM實例,JVM實例啓動時默認啓動幾個守護線程,好比:垃圾回收的線程,而 main 方法的執行是在一個單獨的非守護線程中執行的。只要非守護線程結束JVM實例就銷燬了。數據結構
那麼在Java類main函數運行過程當中,JVM的工做原理以下:多線程
- 根據系統環境變量,建立裝載JVM的環境與配置;
- 尋找JRE目錄,尋找jvm.dll,並裝載jvm.dll;
- 根據JVM的參數配置,如:內存參數,初始化jvm實例;
- JVM實例產生一個 引導類加載器實例(Bootstrap Loader),加載Java核心庫,而後引導類加載器自動加載 擴展類加載器(Extended Loader),加載Java擴展庫,最後擴展類加載器自動加載 系統類加載器(AppClass Loader),加載當前的Java類;
- 當前Java類加載至內存後,會通過 驗證、準備、解析 三步,將Java類中的 類型信息、屬性信息、常量池 存放在方法區內存中,方法指令直接保存到棧內存中,如:main函數;
- 執行引擎開始執行棧內存中指令,因爲main函數是靜態方法,因此不須要傳入實例,在類加載完畢以後,直接執行main方法指令;
- main函數執行主線程結束,隨之守護線程銷燬,最後JVM實例被銷燬;
類加載是Java程序運行的第一步,在java.lang包裏有個ClassLoader類,ClassLoader 的基本目標是 對類的請求提供服務,按需動態裝載類和資源 ,只有當一個類要使用 (1. Class.forName();2. 調用類的靜態方法;3. 使用 new 關鍵字來實例化一個類) 的時候,類加載器纔會加載這個類並初始化。當咱們自定義類加載器加載類文件時(繼承自ClassLoader類,只需覆蓋 findClass方法,便可),其類加載機制以下:併發
當自定義類加載器加載類時,會調用loadClass方法加載類,而因爲類加載的雙親委託模式,會將類的加載代理給父類加載器:系統類加載器來完成,依次類推至最頂層引導類加載器加載,若是父類加載器沒有加載到類,則最終返回由自定義類加載器加載類,經過雙親委託模式,對於 Java 核心庫的類的加載工做由引導類加載器來統一完成,保證了 Java 應用所使用的都是同一個版本的 Java 核心庫的類。app
須要說明一下Java虛擬機是如何斷定兩個Java 類是相同的。Java 虛擬機不只要看類的全名是否相同,還要看加載此類的類加載器是否同樣。只有二者都相同的狀況,才認爲兩個類是相同的。即使是一樣的字節代碼,被不一樣的類加載器加載以後所獲得的類,也是不一樣的。然而,類加載器又分初始類加載器和定義類加載器,因爲類加載的代理模式,初始類加載器並不必定是定義類加載器,因此確切的說,斷定兩個 Java 類是否相同的, 哪一個類加載器啓動類的加載過程並不重要,重要的是最終定義這個類的加載器。jvm
JVM內存主要是指運行數據區,粗略的分爲:堆、棧,細緻區分五部分:方法區、堆、棧、程序計數器、本地方法棧。
程序計數器: 線程私有,記錄線程所執行的虛擬機字節碼指令的地址;若是正在執行的是native方法,這個計數值則爲空。惟一一個在Java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域。
Java虛擬機棧: 線程私有,描述Java方法執行的內存模型:每一個方法在執行的同時都會建立一個棧幀,用於存儲 局部變量表,操做數棧,動態連接,方法出口 等信息。每個方法從調用直至執行完成的過程,就對應這一個棧幀在虛擬機棧中入棧到出棧的過程。棧幀是方法運行時的基礎數據結構。若是線程請求棧深度大於虛擬機所容許的深度,拋出StackOverflowError異常;若是虛擬機棧動態擴展時沒法申請到足夠的內存,拋出 OutOfMemoryError異常。
本地方法棧: 線程私有,描述native方法執行的內存模型。有的虛擬機(如Sun HotSpot虛擬機)直接就把 本地方法棧和虛擬機棧 合二爲一。
Java堆: 線程共享,存放對象實例及數組,是垃圾收集器管理的主要區域,採用分代收集策略,因此Java堆會細分爲新生代和老年代。根據Java虛擬機規範的規定,Java堆能夠處於物理上不連續的內存空間中,只要邏輯上連續的便可。若是堆中沒有內存完成實例分配,而且堆也沒法再擴展時,拋出OutOfMemoryError異常。
方法區: 線程共享,存儲已被虛擬機加載的類信息、常量池、靜態變量、即時編譯器編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,可是它卻有一個別名叫Non-Heap(非堆),目的應該是與Java堆區分開來。若是方法區沒法知足內存分配需求,拋出OutOfMemoryError異常。對於HotSpot虛擬機,方法區被稱爲永久代,本質上二者不等價,僅僅是由於HotSpot虛擬機設計團隊選擇把GC分代收集擴至方法區,或者說使用永久代來實現方法區而已。
JVM堆通常又能夠分爲如下三部分:Young 新生代、Tenured 老年代、Perm 永久代;
Perm永久代主要保存class,method,filed對象,這部分的空間通常不會溢出,除非一次性加載了不少的類,不過在涉及到熱部署的應用服務器的時候,有時候會遇到java.lang.OutOfMemoryError : PermGen space 的錯誤。
Tenured老年代主要保存生命週期長的對象,屢次未被GC掉的對象;
Young新生代主要保存新生成對象,根據JVM的策略,在通過幾回垃圾收集後,而沒有被垃圾回收的對象將被移動到Tenured區間。有時候該區常常會遇到java.lang.OutOfMemoryError :Java heap space的錯誤。
-Xms:指定了JVM初始啓動之後 初始化內存;
-Xmx:指定JVM堆得 最大內存,在JVM啓動之後,會分配-Xmx參數指定大小的內存給JVM,可是不必定所有使用,JVM會根據-Xms參數來調節真正用於JVM的內存;
-Xmn:參數設置了新生代的大小;老年代等於-Xmx減去-Xmn;
-XX:Xss:參數設置了永久代的大小;
直接內存: 不是虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域。可是這部份內存也被頻繁的使用,並且也可能致使OutOfMemoryError異常出現。例如:NIO類,引入了一種基於通道(Channel)與緩衝區(Buffer)的IO方式,它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在Java堆中的DirectByteBuffer對象做爲這塊內存的引用進行操做。這樣能在一些場景中顯著提升性能,由於避免了Java堆和Native堆中來回複製數據。直接內存雖然不會受到Java堆大小的限制,可是,既然是內存,仍會受到本機總內存大小及處理器尋址空間的限制。
JVM參數:
-XX:+HeapDumpOnOutOfMemoryError:可以讓虛擬機在內存溢出異常時Dump出當前的內存堆轉儲快照以便過後分析;
-Xss:設置線程棧大小;
-XX:PermSize:設置永久代初始大小;
-XX:MaxPermSize:設置永久代最大大小;
-XX:MaxDirectMemorySize:設置直接內存大小,默認與Java堆最大值同樣;
引用計數算法 存儲對特定對象的全部引用數,也就是說,當應用程序建立引用以及引用超出範圍時,JVM必須適當增減引用數。當某對象的引用數爲0時,即可以進行垃圾收集。
優勢:實現簡單、效率高;
缺點:很難解決對象之間相互引用問題;
可達性分析算法 經過一系列的稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。
可做爲GC Roots的對象包括:
- 虛擬機棧(棧幀的本地變量表)中引用的對象;
- 方法區中類靜態屬性引用的對象;
- 方法區中常量引用的對象;
- 本地方法棧中JNI(即通常說的是native方法)引用的對象;
任何一個對象的finalize()方法都僅會被系統自動調用一次。若是對象面臨下一次回收,它的finalize()方法不會被再次執行。建議避免使用該方法。
GC即垃圾收集機制是指JVM用於釋放那些再也不使用的對象所佔用的內存。經常使用機制:
標記-清除算法【適用於老年代】 首先根據可達性分析算法,標記出全部須要回收的對象,在標記完成後統一回收全部標記的對象。
缺點:效率問題:標記、清除兩個過程效率都低;空間問題:標記清除以後產生大量不連續的內存碎片,空間碎片太多可能會致使之後在程序運行過程當中,須要分配大對象時,沒法找到足夠的連續內存而不得不提早觸發另外一次垃圾收集動做。
複製算法【空間換時間,適用於對象存活率低的新生代】 將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,而後再把已使用過的內存空間一次清理掉。
優勢:實現簡單、效率高、無內存碎片;
缺點:內存使用率低,太浪費。
因爲新生代中的對象98%是朝生夕死的,因此 將內存分爲一塊較大的內存、兩塊較小的內存。每次使用一塊大內存和一塊小內存,當垃圾回收時,會將該大內存和小內存中存活的對象一次性的複製到另一塊小內存中,最後清理掉該大內存、小內存。可是若是對象的存活率較高,那麼當複製對象至另外一塊小內存時,該小內存空間會不夠用,則須要依賴其餘內存(老年代)進行分配擔保。
當對象的存活率較高時,複製算法要進行較多的複製操做,效率會變低。
標記-整理算法【適用於老年代】
標記過程與「標記-清除」算法同樣,惟一區別是在後續步驟不是直接對內存進行清除,而是先讓全部活着的對象都向一端移動,而後直接清除掉端邊界之外的內存。
分代收集策略 通常是把Java堆分紅 新生代和老年代,這樣就能夠根據各個年代的特色採用最適當的收集算法。
新生代中對象存活率低,採用複製算法,有老年代對它進行分配擔保。
老年代中對象存活率高,無額外空間對它進行分配擔保,必須採用 「標記-清除」或「標記-整理」 算法。
回收方法區(永久代) 不少人認爲方法區(永久代)是沒有垃圾收集的,Java虛擬機規範中確實說過能夠不要求虛擬機在方法區實現垃圾收集,何況在方法區中進行垃圾收集性價比很低;
永久代的垃圾收集主要回收兩方面: 廢棄常量和無用的類;
廢棄常量:判斷常量池的對象是否還存在任何引用;
無用的類:(1)該類的全部實例都被回收;(2)該類的ClassLoader已被回收;(3)該類的Class對象沒有任何引用;
JDK1.7 Update14以後的HotSpot虛擬機正式提供了G1收集器;
Serial收集器: 單線程、Stop The Wold、Client模式默認新生代收集器、 複製算法;
ParNew收集器: Serial多線程版、並行、Stop The Wold、Server模式默認新生代收集器、只有該收集器能與CMS收集器配合、 複製算法;
ParNew收集器也是使用:
-XX:+UseConcMarkSweepGC 選項後的默認新生代收集器;
-XX:+UserParNewGC 選項來強制指定它;
-XX:ParallelGCThreads 參數來限制垃圾收集的線程數,默認開啓的線程數與CPU的數量相等;
Parallel Scavenge收集器: 多線程、並行、Stop The Wold、 新生代收集器、Server模式、 複製算法; CMS等收集器的關注點是 儘量地縮短垃圾收集時用戶線程的停頓時間,而 該收集器關注的是達到一個可控制的吞吐量;
所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),吞吐量與垃圾收集時間成反比;
停頓時間越短就越適合須要與用戶交互的程序,而高吞吐量則能夠高效率的利用CPU,儘快完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務;
-XX:MaxGCPauseMillis:設置最大垃圾收集停頓時間;
-XX:GCTimeRatio:設置吞吐量大小,大於0且小於100的整數,默認爲99;
-XX:UseAdaptiveSizePolicy:設置打開GC自適應的調節策略,以達到最大的吞吐量;
Serial Old收集器: 單線程、Stop The Wold、 老年代、 標記-整理算法、Client模式;做爲CMS收集器的備選方案,在併發收集發生Concurrent Mode Failure時使用;
Parallel Old收集器: 多線程、Stop The Wold、 老年代、 標記-整理算法、Server模式、並行;適合與Parallel Scavenge配合使用;
CMS收集器: 多線程、Stop The Wold、 老年代、 標記-清除算法、Server模式、併發; 缺點:內存碎片;
-XX:+UseCMSCompactAtFullCollection:默認開啓,用於在CMS收集器頂不住進行Full GC時開啓內存碎片的合併整理過程,內存整理的過程是沒法併發,會致使停頓時間變長;
-XX:CMSFullGCsBeforeCompaction:用於設置執行多少次不壓縮的Full GC後,跟着來一次帶壓縮的GC,默認爲0,表示每次進行Full GC時都進行碎片整理;
G1收集器: 多線程、新老年代、複製+標記-整理算法、併發、Server模式、GC整個堆; 特色:並行與併發、分代收集、空間整理、可預測的停頓;
G1雖然保存了新老年代的概念,但已經不是物理分割了,他們都是一部分Region(不須要連續)的集合;
args="-Dfile.encoding=UTF-8 -J-server -J-Xss128k -J-XX:ThreadStackSize=128 -J-XX:PermSize=64m -J-XX:MaxPermSize=256m -J-verbose:gc -J-XX:+PrintGCDetails -J-XX:+PrintGCTimeStamps -Djava.library.path=${RESIN_HOME}/libexec:/opt/j2sdk/lib:/usr/lib64 -Djmagick.systemclassloader=false -DNO_TIMEOUT"
args="$args -Xdebug - Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9090"
args="$args -Xmn5g -Xms10g -Xmx10g"
args="$args -J-XX:+UseParNewGC -J-XX:+UseConcMarkSweepGC"
複製代碼
Client模式:默認-XX:+UseSerialGC,Serial + Serial Old; Server模式:默認-XX:+UseParallelGC,Parallel Scavenge + Serial Old;
-XX:+PrintGC 打印GC信息
-XX:+PrintGCDetails 打印較爲詳細的GC信息
-XX:+PrintGCTimeStamps 打印GC時間戳,相對於應用程序啓動的時間
複製代碼
Serial:串行收集器,當進行垃圾收集時,會暫停全部線程; Parallel:並行收集器,是串行收集器的多線程版本,多CPU下; ParallelOld:老年代的Parallel版本; ConcMarkSweep:簡稱CMS,是併發收集器,將部分操做與用戶線程併發執行; CMSIncrementalMode:CMS收集器變種,屬增量式垃圾收集器,在併發標記和併發清理時交替運行垃圾收集器和用戶線程; G1:面向服務器端應用的垃圾收集器,計劃將來替代CMS收集器;
跟 Java 堆大小相關的 JVM 內存參數
下面三個 JVM 參數用來指定堆的初始大小和最大值以及堆棧大小:
-Xms 設置 Java 堆的初始化大小
-Xmx 設置最大的 Java 堆大小
-Xss 設置Java線程棧大小
關於打印垃圾收集器詳情的 JVM 參數
-verbose:gc 記錄 GC 運行以及運行時間,通常用來查看 GC 是不是應用的瓶頸
-XX:+PrintGCDetails 記錄 GC 運行時的詳細數據信息,包括新生成對象的佔用內存大小以及耗費時間等
-XX:+PrintGCTimeStamps 打印垃圾收集的時間戳
設置 Java 垃圾收集器行爲的 JVM 參數
-XX:+UseParallelGC 使用並行垃圾收集
-XX:+UseConcMarkSweepGC 使用併發標誌掃描收集 (Introduced in 1.4.1)
-XX:+UseSerialGC 使用串行垃圾收集 (Introduced in 5.0.)
須要提醒的是,但你的應用是很是關鍵的、交易很是頻繁應用時,應該謹慎使用 GC 參數,由於 GC 操做是耗時的,你須要在這之中找到平衡點。
JVM調試參數,用於遠程調試
-Xdebug -Xnoagent
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
複製代碼
用於修改 Perm Gen 大小的 JVM 參數
下面的這三個參數主要用來解決 JVM 錯誤:java.lang.OutOfMemoryError:Perm Gen Space
-XX:PermSize and MaxPermSize
-XX:NewRatio=2 Ratio of new/old generation sizes.
-XX:MaxPermSize=64m Size of the Permanent Generation.
複製代碼
用來跟蹤類加載和卸載的信息
-XX:+TraceClassLoading 和 -XX:+TraceClassUnloading 用來打印類被加載和卸載的過程信息,這個用來診斷應用的內存泄漏問題很是有用。
用於調試目的的 JVM 開關參數
-XX:HeapDumpPath=./java_pid.hprof Path to directory or file name for heap dump.
-XX:+PrintConcurrentLocks Print java.util.concurrent locks in Ctrl-Break thread dump.
-XX:+PrintCommandLineFlags Print flags that appeared on the command line.
複製代碼
若是你的應用追求低停頓,那G1如今已經能夠做爲一個可嘗試的選擇;若是你的應用追求吞吐量,那G1並不會爲你帶來什麼特別的好處;
可達性分析必須在一個能確保一致性的快照中進行,一致性是指在整個分析過程當中整個執行系統必須凍結,不能夠出現分析過程當中對象引用關係還在不斷變化的狀況,該點不知足的話可達性分析結果不許確。因此這點是致使GC進行時必須停頓全部線程的緣由。
新生代GC:Minor GC,指發生在新生代的垃圾收集動做;
老年代GC:Major GC/Full GC,指發生在老年代垃圾收集動做,出現了Major GC,常常會伴隨至少一次的Minor GC。會發生Stop The Wold。
大對象:經過參數 -XX:PretenureSizeThreshold=3145287,令大於這個設置值的對象直接在老年代分配。以免大對象在新生代分配,從而觸發新生代GC。
屢次GC仍存活的對象:當對象每」熬過「一次Minor GC,對象年齡就增長1歲,當對象年齡增長到必定程度 (默認15歲),就會晉升到老年代中。對象晉升老年代的年齡閥值,能夠經過參數 -XX:MaxTenurigThreshold 設置。
動態對象年齡斷定:虛擬機並非永遠地要求對象年齡必須達到MaxTenurigThreshold閥值才能晉升到老年代,若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就能夠直接進入老年代,無須等到MaxTenurigThreshold閥值。
新生代—分配擔保:因爲新生代採用複製收集算法,當新生代Minor GC 時,若是存活對象的大小大於Survivor,依據分配擔保策略會將Survivor沒法容納的對象直接存入老年代。若是老年代仍不可以存放剩餘的對象,則會發生Major GC/Full GC,就會Stop The Wold。
常量池其實就是方法區一個內存空間,虛擬機必須爲每一個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和。以字符串爲例,在Java源代碼中的每個字面值字符串,都會在編譯成class文件階段,造成標誌號爲 8(CONSTANT_String_info)的常量表 。當JVM加載 class文件的時候,會爲對應的常量池創建一個內存數據結構,並存放在方法區中。 以下代碼:
public class Test{
private String str="咱們"。
}
複製代碼
將Test編譯以後造成class文件,那麼在class文件中"咱們"會以一種 CONSTANT_UTF8_info 表的形式存在,字節序列以下:1 0 6 230 136 145 228 187 172 1表示常量表的類型,0 6表示有6個字節的長度。後面6個字節是UTF-8編碼的「咱們」。當JVM運行的時候會將這些常量池的信息加載進方法區。也就是說在運行過程當中內存存儲的"咱們"是UTF-8編碼的。
Java語言的一個很是重要的特色就是 平臺無關性。而使用JVM是實現這一特色的關鍵。通常的高級語言若是要在不一樣的平臺上運行,至少須要編譯成不一樣的目標代碼。而引入JVM後,Java語言在不一樣平臺上運行時不須要從新編譯。Java語言使用JVM屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在JVM虛擬機上運行的目標代碼(字節碼),就能夠在多種平臺上不加修改地運行。JVM在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。
這個異常問題本質緣由是咱們 建立了太多的線程,而能建立的線程數是有限制的,致使了異常的發生。能建立的線程數的具體計算公式以下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
複製代碼
MaxProcessMemory:指的是一個進程的最大內存
JVMMemory:JVM內存
ReservedOsMemory:保留的操做系統內存
ThreadStackSize:線程棧的大小
在java語言裏, 當你建立一個線程的時候,虛擬機會在JVM內存建立一個Thread對象同時建立一個操做系統線程,而這個系統線程的內存用的不是JVMMemory,而是系統中剩下的內存(MaxProcessMemory - JVMMemory - ReservedOsMemory),能夠下面方法解決問題:
(1) 經過設置 -Xmx512m 減小JVM Heap size;
(2) 經過設置 -Xss64k 減小線程佔用的Stack size;
(3) 增長操做系統內存;