JVM是java實現跨平臺的主要依賴就不具體解釋它是什麼了 ,簡單說就是把java的代碼轉化爲操做系統能識別的命令去執行,下面直接講一下它的組成java
1.ClassLoader(類加載器)算法
加載Class 文件到內存中安全
2.ClassArea(方法區)服務器
在類裝載器加載class文件到內存的過程當中,虛擬機會提取其中的類型信息,並將這些信息存儲到方法區。方法區用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。因爲全部線程都共享方法區,所以它們對方法區數據的訪問必須被設計爲是線程安全的。注:常說的String 字符串存儲的常量池 ,存在於方法區內部網絡
3.Heap(堆)多線程
存儲Java程序建立的類實例。全部線程共享,所以設計程序時也要考慮到多線程訪問對象(堆數據)的同步問題。併發
4.Stack(棧)jsp
Java棧是線程私有的。每當啓動一個新線程時,Java虛擬機都會爲它分配一個Java棧。Java棧以幀爲單位保存線程的運行狀態。虛擬機只會直接對Java棧執行兩種操做:以幀爲單位的壓棧或出棧。當線程調用java方法時,虛擬機壓入一個新的棧幀到該線程的java棧中。當方法返回時,這個棧幀被從java棧中彈出並拋棄。一個棧幀包含一個java方法的調用狀態,它存儲有局部變量表、操做棧、動態連接、方法出口等信息。佈局
5.Program Counter Register(程序計數器)性能
一個運行中的Java程序,每當啓動一個新線程時,都會爲這個新線程建立一個本身的PC(程序計數器)寄存器。程序計數器的做用能夠看作是當前線程所執行的字節碼的行號指示器。字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。若是線程正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Natvie方法,這個計數器值則爲空(Undefined)。
6.Native Method Stack(本地方法棧)
本地方法棧與虛擬機棧所發揮的做用是很是類似的,其區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則是爲虛擬機使用到的Native方法服務。任何本地方法接口都會使用某種本地方法棧。當線程調用Java方法時,虛擬機會建立一個新的棧幀並壓入Java棧。然而當它調用的是本地方法時,虛擬機會保持Java棧不變,再也不在線程的Java棧中壓入新的幀,虛擬機只是簡單地動態連接並直接調用指定的本地方法。若是某個虛擬機實現的本地方法接口是使用C鏈接模型的話,那麼它的本地方法棧就是C棧。注:這部分功能產生的內存佔用屬於非堆內容,容易形成非堆內存溢出問題
7.Execution Engine(執行引擎)
負責執行字節碼。方法的字節碼是由Java虛擬機的指令序列構成的。每一條指令包含一個單字節的操做碼,後面跟隨0個或多個操做數。執行引擎執行字節碼時,首先取得一個操做碼,若是操做碼有操做數,取得它的操做數。它執行操做碼和跟隨的操做數規定的動做,而後再取得下一個操做碼。這個執行字節碼的過程在線程完成前將一直持續。
接下來分析一下,
JVM的內存組成和優化(重點)
JVM內存模型:按照JVM規範,JAVA虛擬機在運行時會管理如下的內存區域:
程序計數器:當前線程執行的字節碼的行號指示器,線程私有。
JAVA虛擬機棧:Java方法執行的內存模型,每一個Java方法的執行對應着一個棧幀的進棧和出棧的操做。
本地方法棧:相似「 JAVA虛擬機棧 」,可是爲native方法的運行提供內存環境。
JAVA堆:對象內存分配的地方,內存垃圾回收的主要區域,全部線程共享。可分爲新生代,老生代。
方法區:用於存儲已經被JVM加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據, Hotspot中的「永久代」。
運行時常量池:方法區的一部分,存儲常量信息,如各類字面量、符號引用等。
直接內存:並非JVM運行時數據區的一部分, 可直接訪問的內存, 好比NIO會用到這部分(好多堆外內存溢出就是這個引發)。
按照JVM規範,除了程序計數器不會拋出OOM外,其餘各個內存區域均可能會拋出OOM。
最多見的OOM狀況有如下三種:
java.lang.OutOfMemoryError: Java heap space ------>java堆內存溢出,此種狀況最多見,通常因爲內存泄露或者堆的大小設置不當引發。對於內存泄露,須要經過內存監控軟件查找程序中的泄露代碼,而堆大小能夠經過虛擬機參數-Xms,-Xmx等修改。
java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法區溢出了,通常出現於大量Class或者jsp頁面,或者採用cglib等反射機制的狀況,由於上述狀況會產生大量的Class信息存儲於方法區。此種狀況能夠經過更改方法區的大小來解決,使用相似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,過多的常量尤爲是字符串也會致使方法區溢出。
java.lang.StackOverflowError ------> JAVA虛擬機棧溢出,通常是因爲程序中存在死循環或者深度遞歸調用形成的,棧大小設置過小也會出現此種溢出。能夠經過虛擬機參數-Xss來設置棧的大小。
從最開始的圖片能看出:動態內存分配的部分主要有堆和棧,其它部份內存變化並不大,着重說一下這兩個。
棧內存:
線程私有,生命週期和線程相同,棧由一系列幀組成(所以Java棧也叫作幀棧),幀保存一個方法的局部變量、操做數棧、常量池指針,每一次方法調用建立一個幀,並壓棧。
官方說法:
Java虛擬機棧描述的是Java方法執行的內存模型:每一個方法被調用的時候都會建立一個棧幀,用於存儲局部變量表、操做棧、動態連接、方法出口等信息。每個方法被調用直至執行完成的過程就對應着一個棧幀在虛擬機中從入棧到出棧的過程。
在Java虛擬機規範中,對這個區域規定了兩種異常狀況:
(1)若是線程請求的棧深度太深,超出了虛擬機所容許的深度,就會出現StackOverFlowError(好比無限遞歸。由於每一層棧幀都佔用必定空間,而 Xss 規定了棧的最大空間,超出這個值就會報錯)
(2)虛擬機棧能夠動態擴展,若是擴展到沒法申請足夠的內存空間,會出現OOM
堆內存:
JVM初始分配的內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的內存由-Xmx指 定,默認是物理內存的1/4。默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制;空餘堆內存大於70%時,JVM會減小堆直到 -Xms的最小限制。所以服務器通常設置-Xms、-Xmx相等以免在每次GC 後調整堆的大小。對象的堆內存由稱爲垃圾回收器的自動內存管理系統回收。
詳細解釋堆的組成
Heap = { Old + NEW = {Eden, from, to} },Old 即 年老代(Old Generation),New 即 年輕代(Young Generation)。年老代和年輕代的劃分對垃圾收集影響比較大。
年輕代
全部新生成的對象首先都是放在年輕代。年輕代的目標就是儘量快速的收集掉那些生命週期短的對象。年輕代通常分3個區,1個Eden區,2個Survivor區(from 和 to)。
大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被複制到Survivor區(兩個中的一個),當一個Survivor區滿時,此區的存活對象將被複制到另一個Survivor區,當另外一個Survivor區也滿了的時候,從前一個Survivor區複製過來的而且此時還存活的對象,將可能被複制到年老代。
2個Survivor區是對稱的,沒有前後關係,因此同一個Survivor區中可能同時存在從Eden區複製過來對象,和從另外一個Survivor區複製過來的對象;而複製到年老區的只有從另外一個Survivor區過來的對象。並且,由於須要交換的緣由,Survivor區至少有一個是空的。特殊的狀況下,根據程序須要,Survivor區是能夠配置爲多個的(多於2個),這樣能夠增長對象在年輕代中的存在時間,減小被放到年老代的可能。
針對年輕代的垃圾回收即 Young GC。
年老代
在年輕代中經歷了N次(可配置)垃圾回收後仍然存活的對象,就會被複制到年老代中。所以,能夠認爲年老代中存放的都是一些生命週期較長的對象。
針對年老代的垃圾回收即 Full GC。
內存申請過程以下:
JVM會試圖爲相關Java對象在年輕代的Eden區中初始化一塊內存區域(注:若是新生成的對象很大,佔用連續內存則直接放在老年代)。
當Eden區空間足夠時,內存申請結束。不然執行下一步。
JVM試圖釋放在Eden區中全部不活躍的對象(Young GC)。釋放後若Eden空間仍然不足以放入新對象,JVM則試圖將部分Eden區中活躍對象放入Survivor區。
Survivor區被用來做爲Eden區及年老代的中間交換區域。當年老代空間足夠時,Survivor區中存活了必定次數的對象會被移到年老代。
當年老代空間不夠時,JVM會在年老代進行徹底的垃圾回收(Full GC)。
Full GC後,若Survivor區及年老代仍然沒法存放從Eden區複製過來的對象,則會致使JVM沒法在Eden區爲新生成的對象申請內存,即出現「Out of Memory」。
下面部分參數說明爲網絡蒐集,出現錯誤請指出
參數說明
-Xmx3550m:設置JVM最大堆內存爲3550M。
-Xms3550m:設置JVM初始堆內存爲3550M。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。
-Xss128k:設置每一個線程的棧大小。JDK5.0之後每一個線程棧大小爲1M,以前每一個線程棧大小爲256K。應當根據應用的線程所需內存大小進行調整。在相同物理內存下,減少這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成,經驗值在3000~5000左右。須要注意的是:當這個值被設置的較大(例如>2MB)時將會在很大程度上下降系統的性能。
-Xmn2g:設置年輕代大小爲2G。在整個堆內存大小肯定的狀況下,增大年輕代將會減少年老代,反之亦然。此值關係到JVM垃圾回收,對系統性能影響較大,官方推薦配置爲整個堆大小的3/8。
-XX:NewSize=1024m:設置年輕代初始值爲1024M。
-XX:MaxNewSize=1024m:設置年輕代最大值爲1024M。
-XX:PermSize=256m:設置持久代初始值爲256M。
-XX:MaxPermSize=256m:設置持久代最大值爲256M。
-XX:NewRatio=4:設置年輕代(包括1個Eden和2個Survivor區)與年老代的比值。表示年輕代比年老代爲1:4。
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的比值。表示2個Survivor區(JVM堆內存年輕代中默認有2個大小相等的Survivor區)與1個Eden區的比值爲2:4,即1個Survivor區佔整個年輕代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一個對象若是在Survivor區(救助空間)移動了7次尚未被垃圾回收就進入年老代。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代,對於須要大量常駐內存的應用,這樣作能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象在年輕代存活時間,增長對象在年輕代被垃圾回收的機率,減小Full GC的頻率,這樣作能夠在某種程度上提升服務穩定性。
部分參數優先級
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3組參數均可以影響年輕代的大小,混合使用的狀況下,優先級是什麼?
以下:
高優先級:-XX:NewSize/-XX:MaxNewSize
中優先級:-Xmn(默認等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低優先級:-XX:NewRatio
推薦使用-Xmn參數,緣由是這個參數簡潔,至關於一次設定 NewSize/MaxNewSIze,並且二者相等,適用於生產環境。-Xmn 配合 -Xms/-Xmx,便可將堆內存佈局完成。
-Xmn參數是在JDK 1.4 開始支持。
垃圾回收器選擇
JVM給出了3種選擇:串行收集器、並行收集器、併發收集器。串行收集器只適用於小數據量的狀況,因此生產環境的選擇主要是並行收集器和併發收集器。
默認狀況下JDK5.0之前都是使用串行收集器,若是想使用其餘收集器須要在啓動時加入相應參數。JDK5.0之後,JVM會根據當前系統配置進行智能判斷。
串行收集器
-XX:+UseSerialGC:設置串行收集器。
並行收集器(吞吐量優先)
-XX:+UseParallelGC:設置爲並行收集器。此配置僅對年輕代有效。即年輕代使用並行收集,而年老代仍使用串行收集。
-XX:ParallelGCThreads=20:配置並行收集器的線程數,即:同時有多少個線程一塊兒進行垃圾回收。此值建議配置與CPU數目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式爲並行收集。JDK6.0開始支持對年老代並行收集。
-XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間(單位毫秒)。若是沒法知足此時間,JVM會自動調全年輕代大小,以知足此時間。
-XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動調全年輕代Eden區大小和Survivor區大小的比例,以達成目標系統規定的最低響應時間或者收集頻率等指標。此參數建議在使用並行收集器時,一直打開。
併發收集器(響應時間優先)
-XX:+UseConcMarkSweepGC:即CMS收集,設置年老代爲併發收集。CMS收集是JDK1.4後期版本開始引入的新GC算法。它的主要適合場景是對響應時間的重要性需求大於對吞吐量的需求,可以承受垃圾回收線程和應用線程共享CPU資源,而且應用中存在比較多的長生命週期對象。CMS收集的目標是儘可能減小應用的暫停時間,減小Full GC發生的概率,利用和應用程序線程併發的垃圾回收線程來標記清除年老代內存。
-XX:+UseParNewGC:設置年輕代爲併發收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,因此無需再設置此參數。
-XX:CMSFullGCsBeforeCompaction=0:因爲併發收集器不對內存空間進行壓縮和整理,因此運行一段時間並行收集之後會產生內存碎片,內存使用效率下降。此參數設置運行0次Full GC後對內存空間進行壓縮和整理,即每次Full GC後馬上開始壓縮和整理內存。
-XX:+UseCMSCompactAtFullCollection:打開內存空間的壓縮和整理,在Full GC後執行。可能會影響性能,但能夠消除內存碎片。
-XX:+CMSIncrementalMode:設置爲增量收集模式。通常適用於單CPU狀況。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代內存空間使用到70%時就開始執行CMS收集,以確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發生。
其它垃圾回收參數
-XX:+ScavengeBeforeFullGC:年輕代GC優於Full GC執行。
-XX:-DisableExplicitGC:不響應 System.gc() 代碼。
-XX:+UseThreadPriorities:啓用本地線程優先級API。即便 java.lang.Thread.setPriority() 生效,不啓用則無效。
-XX:SoftRefLRUPolicyMSPerMB=0:軟引用對象在最後一次被訪問後能存活0毫秒(JVM默認爲1000毫秒)。
-XX:TargetSurvivorRatio=90:容許90%的Survivor區被佔用(JVM默認爲50%)。提升對於Survivor區的使用率。
輔助信息參數設置 -XX:-CITime:打印消耗在JIT編譯的時間。 -XX:ErrorFile=./hs_err_pid.log:保存錯誤日誌或數據到指定文件中。 -XX:HeapDumpPath=./java_pid.hprof:指定Dump堆內存時的路徑。 -XX:-HeapDumpOnOutOfMemoryError:當首次遭遇內存溢出時Dump出此時的堆內存。 -XX:OnError=";":出現致命ERROR後運行自定義命令。 -XX:OnOutOfMemoryError=";":當首次遭遇內存溢出時執行自定義命令。 -XX:-PrintClassHistogram:按下 Ctrl+Break 後打印堆內存中類實例的柱狀信息,同JDK的 jmap -histo 命令。 -XX:-PrintConcurrentLocks:按下 Ctrl+Break 後打印線程棧中併發鎖的相關信息,同JDK的 jstack -l 命令。 -XX:-PrintCompilation:當一個方法被編譯時打印相關信息。 -XX:-PrintGC:每次GC時打印相關信息。 -XX:-PrintGCDetails:每次GC時打印詳細信息。 -XX:-PrintGCTimeStamps:打印每次GC的時間戳。 -XX:-TraceClassLoading:跟蹤類的加載信息。 -XX:-TraceClassLoadingPreorder:跟蹤被引用到的全部類的加載信息。 -XX:-TraceClassResolution:跟蹤常量池。 -XX:-TraceClassUnloading:跟蹤類的卸載信息。