1. 程式人生 > >筆記:深入理解JVM 第2章 Java記憶體區域與記憶體溢位

筆記:深入理解JVM 第2章 Java記憶體區域與記憶體溢位

1、JVM 執行時資料區

所有執行緒共享的資料區:方法區(持久代)、堆區

執行緒隔離的資料區:程式計數器、Java虛擬機器棧區

堆區構成:新生代 ( 由Eden, From Survivor, To Survivor 構成)、老生代

執行時常量池:方法區一部分,用於存放編譯期生成的各種字面量和符號引用

直接記憶體:不是JVM 執行時資料區的一部分,不受Java堆大小限制,但是受實體記憶體限制,也會丟擲 OutOfMemoryError。Java NIO 中的DirectByteBuffer 使用了直接記憶體。

2、OutOfMemoryError 案例

(1)、堆溢位

在物件數量達到最大堆容量限制後,發生堆記憶體溢位異常 OutOfMemoryError:

Java heap space 。

JVM配置:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

程式碼:

public static void main(String[] args) {
		 List<Object>  list = new LinkedList<Object>();
		 for(int i=0;i<Long.MAX_VALUE;i++)
		 {
			 list.add(new XX());			 
		 }
	}

輸出:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid4192.hprof ...
Heap dump file created [37582435 bytes in 0.265 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.util.LinkedList.linkLast(Unknown Source)
	at java.util.LinkedList.add(Unknown Source)
	at chapter2.HeapOver.main(HeapOver.java:14)

使用Eclipse Memory Analyzer 分析生成的Dump檔案:java_pid4192.hprof

快照


(2).棧溢位

執行緒的請求棧深度大於虛擬機器所允許最大深度,將丟擲StackOverflowError

如果虛擬機器在擴充套件棧時候無法申請得到足夠的記憶體空間,將丟擲OutOfMemoryError

JVM配置:

-Xss128k
單執行緒程式碼: 
public class StackOverExam1 {

	public int stackLength = 1;

	public void methed1() {
		System.out.println("Stack Length:" + stackLength++);
		if(stackLength < Long.MAX_VALUE) 
                {  
                    methed1();  
                }
	}

	public static void main(String[] args) {
		StackOverExam1 exam = new StackOverExam1();
		exam.methed1();
	}

}


丟擲異常:

Stack Length:987
Stack Length:988
Exception in thread "main" java.lang.StackOverflowError
	at java.nio.Buffer.<init>(Unknown Source)
	at java.nio.CharBuffer.<init>(Unknown Source)
	at java.nio.HeapCharBuffer.<init>(Unknown Source)
	at java.nio.CharBuffer.wrap(Unknown Source)
	at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
	at sun.nio.cs.StreamEncoder.write(Unknown Source)

在單執行緒下,無論是棧幀太大還是虛擬機器容量太小,當記憶體無法分配時候,虛擬機器丟擲的都是StackOverflowError。

多執行緒情況下,-Xss設定得越大,即為每個執行緒棧分配記憶體越大,越容易丟擲 OutOfMemoryError: unable to create new native thread

多執行緒時候JVM配置:
-Xss2m

多執行緒程式碼:
public static void main(String[] args) {
		 
		for(long i=0;i<Long.MAX_VALUE;i++)
		{
			System.out.println("Thread Num:"+ (++i));
			new Thread(new Runnable()
			{

				@Override
				public void run() {
					while(true)
					{
						
					}					
				}
				
			}).start();
		}
	}

丟擲異常:
ava.lang.OutOfMemoryError: unable to create new native thread


(3). 物件常量池和方法區溢位

丟擲 OutOfMemory: PermGen space

常量池溢位例子:

String.intern() 是一個Native方法,其作用是:若字串常量池包含此String物件,則返回該物件;否則將其加入到常量池中,再返回。 

JVM配置:

-XX:PermSize=10m -XX:MaxPermSize=10m
程式碼:
public class RuntimeConstPoolOver {
 
	public static void main(String[] args) {
		List<String> list = new LinkedList<String>();
		for (int i = 0; i < Long.MAX_VALUE; i++) {
			//System.out.println(i);
			list.add(String.valueOf(i).intern());
		}
	}

}

JDK1.6 輸出: 
 java.lang.OutOfMemoryError: PermGen space
JDK 1.7 不會有錯誤

方法區溢位例子:

Spring、Hibernate 等框架會使用CGLib技術,將動態生成的Class載入方法區,當方法區太小時,引發 

 java.lang.OutOfMemoryError: PermGen space

(4).直接記憶體溢位

DirectMemory 容量可通過 -XX: MaxDirectMemorySize 制定,若無制定則與JVM堆的最大值(-Xmx) 一樣。

判斷是否由DirectMemory的溢位條件:

a. Heap Dump 檔案不會看到明顯異常,檔案很小

b. 程式中直接或間接使用了NIO

例子JVM配置:

-Xmx20M -XX:MaxDirectMemorySize=20M
程式碼:
import java.lang.reflect.Field;
import sun.misc.Unsafe;

public class DirectMemoryOver {

	public static void main(String[] args) throws IllegalArgumentException,
			IllegalAccessException {

		long _100M = 100 * 1024 * 1024;
		Field f = Unsafe.class.getDeclaredFields()[0];
		f.setAccessible(true);
		Unsafe unsafe = (Unsafe) f.get(null);
		for (int i = 0; i < Long.MAX_VALUE; i++) {
            System.out.println(i);
			unsafe.allocateMemory(_100M);
		}
	}

}

輸出:
126
Exception in thread "main" java.lang.OutOfMemoryError
	at sun.misc.Unsafe.allocateMemory(Native Method)
	at chapter2.DirectMemoryOver.main(DirectMemoryOver.java:17)