1. 程式人生 > 實用技巧 >JVM 學習筆記(四)

JVM 學習筆記(四)

回顧:

  在之前的文章中,我們主要體現了當堆記憶體設定的比較小的情況下,比如:-Xmx20M -Xms20M,在專案執行的過程中,不斷往記憶體中去新增物件,

這時候就會出現OOM,也就是記憶體溢位,本文章將展示方法區和虛擬機器器棧記憶體溢位的情況。

方法區記憶體溢位:

  為了使方法區記憶體溢位,我們將JVM的引數調整為:-XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M。然後不斷的往方法區中新增class資訊,

前面我們介紹了方法區的作用是:用於儲存已被虛擬機器器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。

程式碼如下:

//pom.xml中新增如下依賴
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency> public class MyMetaspace extends ClassLoader {
public static List<Class<?>> createClasses() {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (int i = 0; i < 10000000; ++i) {
ClassWriter cw = new ClassWriter(0);
cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mw.visitVarInsn(Opcodes.ALOAD, 0);
mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
mw.visitInsn(Opcodes.RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
Metaspace test = new Metaspace();
byte[] code = cw.toByteArray();
Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
classes.add(exampleClass);
}
return classes;
}
}

  執行程式碼,果然會包方法區記憶體溢位:

虛擬機器器棧記憶體溢位:

  前面我們介紹虛擬機器器棧是一個執行緒執行的區域,儲存著一個執行緒中方法的呼叫狀態。換句話說,一個Java執行緒的執行狀態,由一個虛擬機器器棧來儲存,所以虛擬機器器棧肯定是執行緒私有的,獨有的,隨著執行緒的建立而建立。每一個被執行緒執行的方法,為該棧中的棧幀,即每個方法對應一個棧幀。呼叫一個方法,就會向棧中壓入一個棧幀;一個方法呼叫完成,就會把該棧幀從棧中彈出。

  程式碼演示:

public class StackDemo {
public static long count = 0; public static void method(long i) {
System.out.println(count++);
method(i);
} public static void main(String[] args) {
method(1);
}
}

  結果:

  理解和說明:

  Stack Space用來做方法的遞迴呼叫時壓入Stack Frame(棧幀)。所以當遞迴呼叫太深的時候,就有可能耗盡Stack Space,爆出StackOverflow的錯誤。   -Xss128k:設定每個執行緒的堆疊大小。JDK 5以後每個執行緒堆疊大小為1M,以前每個執行緒堆疊大小為256K。根據應用的線

程所需記憶體大小進行調整。在相同實體記憶體下,減小這個值能生成更多的執行緒。但是作業系統對一個程式內的執行緒數還是有 限制的,不能無限生成,經驗值在3000~5000左右。   執行緒棧的大小是個雙刃劍,如果設定過小,可能會出現棧溢位,特別是在該執行緒內有遞迴、大的迴圈時出現溢位的可能性更 大,如果該值設定過大,就有影響到建立棧的數量,如果是多執行緒的應用,就會出現記憶體溢位的錯誤。