文章目錄
- Java虛拟機
-
- 什麼是java虛拟機
- Java記憶體結構
-
- 什麼是java記憶體結構
- 堆
-
- 堆記憶體
- 堆記憶體參數配置
-
- 什麼是虛拟機參數配置?
- 堆的配置參數
- 顯示堆參數例子
- 配置堆參數-初始值和最大值
- 配置堆參數-設定新生代參數
- 配置堆參數-設定新生代與老年代比例
- 堆溢出解決辦法
- Tomcat記憶體溢出在catalina.sh修改堆記憶體大小
- 棧
-
- 什麼是棧溢出
- 棧溢出解決方法
Java虛拟機
什麼是java虛拟機
所謂虛拟機,就是一台虛拟的機器,它是一款軟體,用來執行一系列虛拟計算指令,他們完全是對實體計算的仿真,提供了一個可以運作完整作業系統的平台。
java虛拟機,它專門為執行單個計算程式而計算,在java虛拟機中執行的指令我們稱為java位元組碼指令。
在虛拟機上運作的軟體都限制于虛拟機提供的資源中。
Java記憶體結構
什麼是java記憶體結構
JVM虛拟機存儲空間。
java檔案編譯後變為class檔案,classLoader類加載器讀取class檔案,配置設定資源到哪個記憶體空間去。
記憶體空間:
-
方法區
方法區又稱為永久區,static關鍵字修飾的存放在方法區,方法區一般存放常量資訊,當class檔案被加載的時候,方法區就會被初始化,方法區是全局的。
-
堆
建立(new)的對象,數組等等,存放在堆記憶體中。堆記憶體所有線程都會共享,
-
棧
存放定義基本局部變量,好處是代碼運作完畢,自動釋放記憶體。每個線程私有,互不共享,棧不會産生線程安全問題。類的方法存放在棧中。
-
本地方法棧
主要作用是調用C語言,安卓開發中,底層可能會用C語言,java去使用C語言用到JNI技術,會用到本地方法棧。
PC寄存器:
每個線程都有PC寄存器,每個線程的PC寄存器是私有的。執行指令的指針
執行引擎:
負責執行位元組碼檔案
調優政策主要是堆和垃圾回收機制。
堆
堆記憶體
new出來的對象都會存放在堆記憶體中,堆記憶體中配置設定兩個區,分為新生代和老年代,分代作用是利于垃圾回收機制
新生代,垃圾回收機制不經常回收的區域。
老年代:如果對象被頻繁的使用,将對象放入到老年代中。
新生代裡又分為eden、s0、s1區。其中s0和s1區大小相等,目的是垃圾回收複制算法。其中s0、s1又稱為from/to區
- eden:剛建立的對象存放在eden區
- s0、s1:在eden區的對象經常被使用後轉移到s0或者s1區。
- 老年代:如果s0或者s1區的對象仍在頻繁使用就晉升到老年代區,到了老年代的對象不會回到新生代,是不可逆的轉移過程。
垃圾回收機制主要回收新生代。
調優:盡量減少垃圾回收機制的次數。在web系統中盡量減少常量資訊。盡量減少老年代的回收次數,提高新生代的回收次數。
堆記憶體參數配置
什麼是虛拟機參數配置?
使用給定的參數執行java虛拟機,就可以在系統運作時列印相關日志,用于分析實際問題。
主要圍繞堆、棧、方法區進行配置
堆的配置參數
參數 | 說明 |
---|---|
-XX:+PrintGC | 每次觸發GC時列印相關日志 |
-XX:+UserSerialGC | 串行回收 |
-XX:+PrintGCDetails | 更詳細的GC日志 |
-Xms | 堆初始值 |
-Xmx | 堆最大可用值 |
-Xmn | 新生代堆最大可用值 |
-XX:SurvivorRatio | 用來設定新生代中eden空間和trom/to空間的比例 |
實際工作中,我們一定要把初始的堆大小與最大堆大小相等,這樣的好處是可以減少程式運作時垃圾回收次數,進而提高效率
顯示堆參數例子
import java.text.DecimalFormat;
public class Test1 {
public static void main(String[] args) throws InterruptedException {
// 建立byte數組
byte[] bytes1 = new byte[1024 * 1024];
System.out.println("配置設定1M記憶體");
jvmInfo();
Thread.sleep(5000);
byte[] bytes2 = new byte[4 * 1024 * 1024];
System.out.println("配置設定4M記憶體");
jvmInfo();
}
/**
* 比特轉換為M
*
* @param bt 多少比特
* @return {@link String} 多少M,保留兩位小數
*/
static public String toM(long bt) {
float num = (float) bt / (1024 * 1024);
// 設定小數格式
DecimalFormat df = new DecimalFormat("0.00");
return df.format(num);
}
/**
* 顯示jvm資訊
*/
static public void jvmInfo() {
// 最大記憶體配置資訊 機關K
long maxMemory = Runtime.getRuntime().maxMemory();
System.out.println("最大記憶體:" + toM(maxMemory) + " M");
// 目前空閑記憶體
long freeMemory = Runtime.getRuntime().freeMemory();
System.out.println("目前空閑記憶體:" + toM(freeMemory) + " M");
// 傳回Java虛拟機中的記憶體總量
long totalMemory = Runtime.getRuntime().totalMemory();
System.out.println("記憶體總量:" + toM(totalMemory) + " M");
// 傳回Java虛拟機可用的處理器數量
long availableProcessors = Runtime.getRuntime().availableProcessors();
System.out.println("可用的處理器數量:" + availableProcessors);
}
}
totalMemory()應該是已用記憶體的意思
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxSMfVmepNHL61EVNhXUE9UNFpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL2QDN0QDNwQTM0ETMwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
配置堆參數-初始值和最大值
Edit Configurations
VM arguments添加參數
-Xms5m -Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
運作結果
這一條日志表示GC已經回收了
可以看到GC回收了兩次
我們把參數 -Xms5m -Xmx20m改成 -Xms20m -Xmx20m,初始值和最大值相等,看看結果,就隻回收了一次
為什麼初始值越小,垃圾回收機制次數越多?
初始值越小,當要配置設定較大的記憶體時,空間不夠,配置設定記憶體的時候就越需要進行回收
配置堆參數-設定新生代參數
-Xmn 新生代大小
-XX:SurvivorRatio 設定新生代中eden區與from/to空間的比例
VM arguments添加參數
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
設定新生代大小為2m,eden與from/to比例為2:1
public class Test2 {
public static void main(String[] args) {
byte[] bytes = null;
for (int i = 0; i < 10; i++) {
System.out.println(i);
bytes = new byte[1024*1024];
}
}
}
運作結果,eden與from/to比例為2:1
配置堆參數-設定新生代與老年代比例
-XX:NewRation 設定老年代/新生代的大小
基本政策:盡可能将對象留在新生代,減少老年代的GC次數。
VM arguments添加參數
-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2
-XX:NewRatio=2,則Old Generation是 Yong Generation的2倍,即Yong Generation占據記憶體的1/3,垃圾回收機制主要回收新生代,盡量減少垃圾回收機制的次數,一般Yong Generation設成占據記憶體的1/3或者1/4
堆溢出解決辦法
堆溢出問題模拟
VM arguments設定參數
-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError 列印問題溢出問題
建立10M的List
public static void main(String[] args) {
List<Object> list = new ArrayList<Object>();
for (int i = 0; i < 10; i++) {
list.add(new byte[1024 * 1024]);
}
System.out.println("建立完畢");
}
加堆記憶體大小即可, -Xms30m -Xmx30m
Tomcat記憶體溢出在catalina.sh修改堆記憶體大小
JAVA_OPTS:(可選)在執行任何指令時使用的Java運作時選項。JAVA_OPTS中的所有選項應該被Tomcat、停止程序、版本指令等使用。
JAVA_OPTS="-server -Xms800m -Xmx800m -XXNewSize=256M -XX:PermSize=256M -XX:MaxNewSize=512m -XX:MaxPermSize=512m"
棧
什麼是棧溢出
操作變量時無限的遞歸進行調用會造成棧溢出,循環調用方法不會造成棧溢出
棧溢出解決方法
棧溢出問題模拟
public class Test4 {
static int count = 0;
public static void getCount() {
try {
count++;
getCount();
} catch (Throwable e) {
System.out.println("深度:" + count);
e.printStackTrace();
}
}
public static void main(String[] args) {
getCount();
}
}
配置參數隻能增加深度,而不能解決無限遞歸造成的棧溢出問題
-Xss5m 設定最大調用深度
從1萬2千多到26萬多