"Write Once,Run Anywhere"(一次編譯,處處運行)是sun宣傳Java語言所提出的口號。Java語言跨平臺的特性與Java虛擬機的存在密不可分。Java源代碼經過編譯生成.class文件字節碼後再被JVM解釋轉化爲目標機器代碼,處處運行的關鍵與前提就是JVM。因此並非Java語言自己能夠能夠跨平臺,而是在不一樣的平臺都有可讓Java語言運行的環境而已。在能夠運行Java虛擬機的地方都內含一個JVM操做系統,從而使JAVA提供了各類不一樣平臺上的虛擬機制,因而可知導出運行的關鍵就是JVM。java
JVM結構數據結構
不斷建立對象oracle
/**
* VM Args:-Xmx10m
*/
public static void main(String[] args) {
List list = new ArrayList();
while (true){
list.add(new Object());
}
}
複製代碼
結果:
java.lang.OutOfMemoryError: Java heap space
ide
遞歸調用佈局
private void test(){
test();
}
public static void main(String[] args) {
new StackTest().test();
}
複製代碼
結果:
java.lang.StackOverflowError
優化
/**
* VM Args:-Xss2M
* @author zzm
*/
public class JavaVMStackOOM {
private void dontStop(){
while (true){
}
}
public void stackLeakByThread(){
while(true){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
複製代碼
本人運行了一下果真像書(深刻理解Java虛擬機)中所說電腦重啓了,Windows系統的同窗謹慎運行spa
/**
* VM Args:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M (jdk8)
* -XX:PermSize=10M -XX:MaxPermSize=10M(jdk7)
* @author zzm
*/
public class Test {
static class OOMOjbect{}
public static void main(String[] args) {
while(true){
Enhancer eh = new Enhancer();
eh.setSuperclass(OOMOjbect.class);
eh.setUseCache(false);
eh.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object arg0, Method arg1,
Object[] arg2, MethodProxy arg3) throws Throwable {
return arg3.invokeSuper(arg0, arg2);
}
});
eh.create();
}
}
}
複製代碼
本人用的是jdk8,輸出結果:
java.lang.OutOfMemoryError: Metaspace操作系統
對象在內存中佈局能夠分紅三塊區域:對象頭、實例數據和對齊填充
線程
對象頭包括兩部分信息:
設計
存儲內容 | 標誌位 | 狀態 |
對象哈希碼、對象分代年齡 | 01 | 未鎖定 |
指向鎖記錄的指針 | 00 | 輕量級鎖定 |
指向重量級鎖的指針 | 10 | 膨脹(重量級鎖定) |
空,不須要記錄信息 | 11 | GC標記 |
偏向線程ID、偏向時間戳、對象分代年齡 | 01 | 可偏向 |
實例數據就是在程序代碼中所定義的各類類型的字段,包括從父類繼承的,這部分的存儲順序會受到虛擬機分配策略參數和字段在源碼中定義順序的影響
並非必然存在的,沒有特別的含義。因爲HotSpot的自動內存管理要求對象的起始地址必須是8字節的整數倍,即對象的大小必須是8字節的整數倍,對象頭的數據正好是8的整數倍,因此當實例數據不夠8字節整數倍時,須要經過對齊填充進行補全。
使用句柄訪問的話,Java堆中將會劃分出一塊內存來做爲句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息:
使用直接指針訪問的話,Java堆對象的佈局中就必須考慮如何放置訪問類型數據的相關信息,reference中存儲的直接就是對象地址
這兩種對象訪問方式各有優點:
oracle JDK官方默認虛擬機HotSpot採用第二中方式進行對象訪問
《極客時間——楊曉峯Java核心技術36講第一講》 《深刻理解Java虛擬機》