所謂虛擬機器,就是一臺虛擬的機器。他是一款軟體,用來執行一系列虛擬計算指令,大體上虛擬機器可以分爲系統虛擬機器和程式虛擬機器, 大名鼎鼎的Visual Box、Vmare就屬於系統虛擬機器,他們完全是對物理計算的模擬,提供了一個可以執行完整操作系統的軟件平臺。
程式虛擬機器典型程式碼就是Java虛擬機器,它專門爲執行單個計算程式而計算,在Java虛擬機器中執行的指令我們成爲Java
自己碼指令。無論是系統虛擬機器還是程式虛擬機器,在上面執行的軟體都被限制於虛擬機器提供的資源中。
Java發展至今,出現過很多虛擬機器,做初Sun使用的一款叫ClassIc的Java虛擬機器,到現在參照最廣泛的是HotSpot虛擬
機,除了Sum意外,還有BEA的Jrockit,目前Jrockit和HostSopt都被oralce收入旗下,大有整合的趨勢。
堆記憶體用於存放由new建立的物件和陣列。在堆中分配的記憶體,由java虛擬機器自動垃圾回收器來管理。
在堆中產生了一個數組或者物件後,還可以在棧中定義一個特殊的變數,這個變數的取值等於陣列或者物件在堆記憶體中的首地址,在棧中的這個特殊的變數就變成了陣列或者物件的參照變數,以後就可以在程式中使用棧記憶體中的參照變數來存取堆中的陣列或者物件,參照變數相當於爲陣列或者物件起的一個別名,或者代號。
根據垃圾回收機制 機製的不同,Java堆有可能擁有不同的結構,最爲常見的就是將整個Java堆分爲
新生代和老年代。其中新聲帶存放新生的物件或者年齡不大的物件,老年代則存放老年物件。
新生代分爲den區、s0區、s1區,s0和s1也被稱爲from和to區域,他們是兩塊大小相等並且可以互相角色的空間。
絕大多數情況下,物件首先分配在eden區,在新生代回收後,如果物件還存活,則進入s0或s1區,之後每經過一次
新生代回收,如果物件存活則它的年齡就加1,物件達到一定的年齡後,則進入老年代。
Java棧是一塊執行緒私有的空間,一個棧,一般由三部分組成:區域性變數表、操作數據棧和幀數據區
區域性變數表:用於報錯函數的參數及區域性變數
運算元棧:主要儲存計算過程的中間結果,同時作爲計算過程中的變數臨時的儲存空間。
幀數據區:除了區域性變數表和操作數據棧以外,棧還需要一些數據來支援常數池的解析,這裏幀數據區儲存着存取常數池的指針,方便計程式存取常數池,另外當函數返回或出現異常時賣虛擬機器子必須有一個異常處理表,方便發送異常的時候找到異常的程式碼,因此異常處理表也是幀數據區的一部分。
Java方法區和堆一樣,方法區是一塊所有執行緒共用的記憶體區域,他儲存系統的類資訊。
比如類的欄位、方法、常數池等。方法區的大小決定系統可以儲存多少個類。如果系統定義太多的類,導致方法區溢位。虛擬機器同樣會拋出記憶體溢位的錯誤。
方法區可以理解爲永久區。
在虛擬機器執行的過程中,如果可以跟蹤系統的執行狀態,那麼對於問題的故障
排查會有一定的幫助,爲此,在虛擬機器提供了一些跟蹤系統狀態的參數,使用
給定的參數執行Java虛擬機器,就可以在系統執行時列印相關日誌,用於分析實際
虛擬機器參數設定,其實就是圍繞着堆、棧、方法區、進行設定。
參數 | 作用 |
---|---|
-XX:+PrintGC | 每次觸發GC的時候列印相關日誌 |
-XX:+UseSerialGC | 序列回收 |
-XX:+PrintGCDetails | 更詳細的GC日誌 |
-Xms | 堆初始值 |
-Xmx | 堆最大可用值 |
-Xmn | 新生代堆最大可用值 |
-XX:SurvivorRatio | 用來設定新生代中eden空間和from/to空間的比例. |
總結:在實際工作中,我們可以直接將初始的堆大小與最大堆大小相等,
這樣的好處是可以減少程式執行時垃圾回收次數,從而提高效率。
參數: -Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
public class JvmDemo01 {
public static void main(String[] args) throws InterruptedException {
byte[] b1 = new byte[1 * 1024 * 1024];
System.out.println("分配了1m");
jvmInfo();
Thread.sleep(3000);
byte[] b2 = new byte[4 * 1024 * 1024];
System.out.println("分配了4m");
Thread.sleep(3000);
jvmInfo();
}
static private String toM(long maxMemory) {
float num = (float) maxMemory / (1024 * 1024);
DecimalFormat df = new DecimalFormat("0.00");// 格式化小數
String s = df.format(num);// 返回的是String型別
return s;
}
static private void jvmInfo() {
// 最大記憶體
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("maxMemory:" + maxMemory + ",轉換爲M:" + toM(maxMemory));
// 當前空閒記憶體
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("freeMemory:" +freeMemory+",轉換爲M:"+toM(freeMemory));
// 已經使用記憶體
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("totalMemory:" +totalMemory+",轉換爲M"+toM(totalMemory));
}
}
-Xmn | 新生代大小,一般設爲整個堆的1/3到1/4左右 |
-XX:SurvivorRatio | 設定新生代中eden區和from/to空間的比例關係n/1 |
參數: -Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
public class JvmDemo02 {
public static void main(String[] args) {
//-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
byte [] b = null;
for (int i = 0; i < 10; i++) {
b =new byte[1*1024*1024];
}
}
}
-Xms20m -Xmx20m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
-XX:NewRatio=2
總結:不同的堆分佈情況,對系統執行會產生一定的影響,在實際工作中,應該根據系統的特點做出合理的設定,基本策略:儘可能將物件預留在新生代,減少老年代的GC次數。
除了可以設定新生代的絕對大小(-Xmn),可以使用(-XX:NewRatio)設定新生代和老年代的比例:
-XX:NewRatio=老年代/新生代
錯誤原因: java.lang.OutOfMemoryError: Java heap space 堆記憶體溢位
解決辦法:設定堆記憶體大小 -Xms1m –Xmx10m -XX:+HeapDumpOnOutOfMemoryError
public static void main(String[] args) throws InterruptedException {
List<Object> list = new ArrayList<>();
Thread.sleep(3000);
jvmInfo();
for (int i = 0; i < 10; i++) {
System.out.println("i:"+i);
Byte [] bytes= new Byte[1*1024*1024];
list.add(bytes);
jvmInfo();
}
System.out.println("新增成功...");
}
錯誤原因: java.lang.StackOverflowError 棧記憶體溢位
棧溢位 產生於遞回呼叫,回圈遍歷是不會的,但是回圈方法裏面產生遞回呼叫, 也會發生棧溢位。
解決辦法:設定執行緒最大呼叫深度
-Xss5m 設定最大呼叫深度
public class JvmDemo04 {
private static int count;
public static void count(){
try {
count++;
count();
} catch (Throwable e) {
System.out.println("最大深度:"+count);
e.printStackTrace();
}
}
public static void main(String[] args) {
count();
}
}
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"
在JVM啓動參數中,可以設定跟記憶體、垃圾回收相關的一些參數設定,預設情況不做任何設定JVM會工作的很好,但對一些設定很好的Server和具體的應用必須仔細調優才能 纔能獲得最佳效能。
通過設定我們希望達到一些目標:
前兩個目前是相悖的,要想GC時間小必須要一個更小的堆,要保證GC次數足夠少,必須保證一個更大的堆,我們只能取其平衡。