浅入理解JVM
JVM全稱java虛擬機。HelloWorld.java(源代碼) ->Hello.class->010101(2進制編碼),class文件和2進制編碼需要在JVM中運行。
jvm運行時的數據區:
藍色的是數據區,綠色的是指令區。
程序計數器:一塊較小的內存空間,指向當前線程所執行的字節碼指令的地址和行號,每條線程都有一個獨立的程序計數器。唯一不會OOM的區域。
虛擬機棧:線程私有,存儲當前運行方法所需要的數據、指令、返回地址。
每個方法在執行的同時都會創建一個棧幀(Stack Frame)用于存儲局部變量表、操作數棧、動態鏈接(比如存儲的一個接口,運行時會解析找到實現類)、方法出口等信息。一個方法對應一個棧幀。局部變量表存放了各種基本類型、對象引用和returnAddress類型(指向了一條字節碼指令地址)。其中64位長度long 和 double占兩個局部變量空間,其他只占一個。可能出現2種異常:1.線程請求的棧的深度大于虛擬機所允許的深度,將拋出StackOverflowError異常;2.如果虛擬機可以動態擴展,如果擴展時無法申請到足夠的內存,就拋出OutOfMemoryError異常。設置JVM參數”-Xss228k”(即棧大小為228k)。
?本地方法棧:和Java虛擬機棧很類似,不同的是本地方法棧為Native方法服務。在HotSpot虛擬機實現中是把本地方法棧和虛擬機棧合二為一的。同理也會拋出StackOverflowError和OutOfMemoryError。
Java堆:是Java虛擬機所管理的內存中最大的一塊。由所有線程共享,在虛擬機啟動時創建。堆區唯一目的就是存放對象實例。堆中可細分為新生代和老年代,新生代又可分為Eden空間、From Survivor空間、To Survivor空間(8:1:1)。堆無法擴展時,拋出OutOfMemoryError異常。設置JVM參數”?-Xms20M?-Xmx20M“(前者表示初始堆大小20M,后者表示最大堆大小20M)。
方法區:所有線程共享,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。當方法區無法滿足內存分配需求時,拋出OutOfMemoryError。
另外一個說法——永久代(Permanent?Generation),呼應堆的新生代和老年代。方法區和堆的劃分是JVM規范的定義,而不同虛擬機有不同實現,對于Hotspot虛擬機來說,將方法區納入GC管理范圍,這樣就不必單獨管理方法區的內存,所以就有了”永久代“這么一說。設置JVM參數為”-XX:MaxPermSize=20M”(方法區最大內存為20M)。
?在JDK1.7之前運行時常量池邏輯包含字符串常量池存放在方法區, 此時hotspot虛擬機對方法區的實現為永久代
在JDK1.7 字符串常量池被從方法區拿到了堆中, 這里沒有提到運行時常量池,也就是說字符串常量池被單獨拿到堆,運行時常量池剩下的東西還在方法區, 也就是hotspot中的永久代
在JDK1.8 hotspot移除了永久代,取而代之的是”元空間(Metaspace)“,所以在JDK8中虛擬機參數”-XX:MaxPermSize”也就沒有了任何意義,取代它的是”-XX:MetaspaceSize“和”-XX:MaxMetaspaceSize”等, 這時候字符串常量池還在堆, 運行時常量池還在方法區, 只不過方法區的實現從永久代變成了元空間(Metaspace) 。
?
直接內存:并不是虛擬機運行時數據區的一部分,也不是Java虛擬機規范中定義的內存區域。JDK1.4加入了NIO,引入一種基于通道與緩沖區的I/O方式,它可以使用Native函數庫直接分配堆外內存,然后通過一個存儲在Java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。因為避免了在Java堆和Native堆中來回復制數據,提高了性能。當各個內存區域總和大于物理內存限制,拋出OutOfMemoryError異常。
?
Question:? 為什么堆的新生代三個區域的大小比為8:1:1?
因為新生代使用復制回收算法。每次用來存放對象的是Eden區和其中一塊Survivor區。當回收時,將Eden區和Survivor from中還存活著的對象一次性復制到另一塊Survivor to區,復制算法所需要的擔保內存為9:1。之所以Eden區:Survivor from區是8:1,是因為兩個Survivor區的from和to區會進行位置交換。MinorGC后哪個區被清空沒有對象了,這個區就會成為to區,而通過復制算法復制的還存活下的對象所在的那個區,也就是有對象的區即為from。(PS:新生代中Survivor to區內存不夠用時,會觸發老年代的擔保機制進行分配擔保。新生代與老年代默認內存比為1:2)
總結一下就是:
- 用來擔保復制回收算法執行的內存為9:1。(留出一片空地)
- S1:S2應為1:1,可用內存中Eden:S1區為8:1。所以新生代三個區比為8:1:1。(使用的內存中還有一塊要成為下次的空地)
?
JVM基礎的參數和指令
jvm三種參數類型:
- 標配參數(java -version等)
- X參數(-Xint解釋執行,-Xcomp第一次使用就編譯成本地代碼,-Xmixed混合模式先編譯再執行)
- XX參數(分為Boolean和KV類型)
jvm常見命令和參數:
java -XX:+PrintFlagsInitial? ?——查看所有默認參數(:=說明被人工修改)
jps -l? ? ? ?——查看正在運行中的進程號
jinfo -flag 參數 進程號? ? ——查看某進程時候開啟某布爾型參數
jinfo -flags 進程號? ?——查詢系統參數(Non-defauls)和人工參數(Command line)
-XX:+Boolean參數? ? ? ——表示開啟某布爾型參數
-XX:-Boolean參數? ? ? ——表示關閉某布爾型參數
-XX:KV參數=???? ? ? ——表示設置KV類型參數為??
from ??? to ???? ? ? ?——表示從初始默認值到自我期望值(最好一致)
-Xss: 初始棧內存大小?(等價于-XX:ThreadStackSize)
-Xms: 初始堆內存大小 -Xmx:最大堆內存大小(等價于InitialHeapSize 和XX:MaxHeapSize?)
java -XX:+PrintCommandLineFlags -version? ——一種查看配置的方式,可以查看默認使用哪種垃圾回收器
-XX:SurvivorRatio? ——調整新生代eden區對from區和to區的倍數,默認為8:1:1(例如設置SurvivorRatio=6,比例為6:1:1)
-XX: NewRatio ——調整堆中老年代對新生代的占比,默認為2:1(例如設置為NewRatio=3,比例為3:1)
-XX:MaxTenuringThreshold? ——設置升到老年代的最大年齡(默認15,新生區15次gc)
例如:?
-XX:+PrintGCDetails
-XX:MaxTenuringThreshold=15? ?——設置元空間大小為128m
-XX:MetaspaceSize=128m? ?——設置元空間大小為128m
-XX:InitialHeapSize? ——初始化堆內存(默認本機內存的64分之1)
-XX:MaxHeapSize? ——最大堆內存(默認本機內存的4分之1)
-XX:ThreadStackSize ——單個線程棧的大小,查出來單位是k,其他都是b(windows根據本機內存大小一般默認為512k~1024k,linux64為1024k)
?
利用jps -l? 和 jinfo -flag 組合查看某進程的某參數是多少,例如查看元空間大小
在VmOptions設置:-XX:MetaspaceSize=128m
再次查看,元空間大小變成了128m
jinfo -flags pid
?
轉載于:https://www.cnblogs.com/dream2true/p/10828769.html
總結
- 上一篇: 高汤用什么代替?
- 下一篇: 松紧贷正在放款3小时到账