一、JVM的位置及體繫結構 JVM作用在操作系統之上,而Java程式作用在jvm之上,其他的程式則與jvm併列 二、類載入器,及雙親委派機制 1.類載入器 作用:載入Class文件 -> new Student();實例的引用放在棧里,具體的對象放在堆里 點擊查看代碼 package com.Tan ...
一、JVM的位置及體繫結構
JVM作用在操作系統之上,而Java程式作用在jvm之上,其他的程式則與jvm併列二、類載入器,及雙親委派機制
1.類載入器
作用:載入Class文件 -> new Student();實例的引用放在棧里,具體的對象放在堆里點擊查看代碼
package com.Tang.jvm;
public class Car {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
System.out.println(car1.hashCode());
System.out.println(car2.hashCode());
System.out.println(car3.hashCode());
System.out.println("=======");
Class<? extends Car> aClass = car1.getClass();
System.out.println(car1.getClass().hashCode());
System.out.println(car2.getClass().hashCode());
System.out.println(car3.getClass().hashCode());
System.out.println("=======");
ClassLoader classLoader = aClass.getClassLoader();
System.out.println(classLoader);//jak1.8java.lang下有本地的ClassLoader類,因此AppClassLoader在其中
System.out.println(classLoader.getParent());//PlatformClassLoader 存在與jre中的ext文件下
System.out.println(classLoader.getParent().getParent());//Bootstrap中(rt.jar)中沒有找到Car類所以返回的是null
}
}
2.雙親委派機制
原理:在一個類運行之前會在當前載入器類型往上找(bootStrap > PlatformClassLoader >AppClassLoader),然後運行程式之後如果在最頂層沒有找到該類,則又會逐層往下找,直到AppClassLoader點擊查看代碼
package java.lang;
public class String {
//雖然在AppClassLoader中也就是本代碼中有String類,但是在類運行之前還會往上找該類是否存在,最後在bootStrap中也找到了官方的String,最後在這裡運行
public String toString() {
return "hello";
}
public static void main(String[] args) {
String s = new String();
s.toString();
}
}
點擊查看代碼
package com.Tang.jvm;
public class Student {
@Override
public String toString() {
return "hello";
}
public static void main(String[] args) {
Student student = new Student();//在new Student 之後會現在bootstrap中找,發現沒有這個類,然後去ext中去找,發現也沒有,最後只能在當前應用下運行
System.out.println(student.toString());
}
}
三、Native、方法區
1.Native
凡是帶native關鍵字的,說明Java的作用範圍達不到了,==回去調用底層c語言的庫!==會進入本地方法棧,調用本地介面 JNI。 JNI作用:擴展Java的使用,融合不同的編程語言為Java所用!最初:C、C++。java誕生的時候c、C++橫行,想要立足,必須要有調用C、C++的程式,於是它在記憶體區域中專門開闢了一塊標記區域:Native Method Stack ,登記native方法,在最終執行的時候,載入本地方法庫中的方法通過JNI四、堆
1.堆的基本概念
Heap,一個JVM只有一個堆記憶體,堆記憶體的大小是可以調節的。 類載入器讀取了類之後,一般會把類,方法,常量,變數,放到堆中。類,方法,常量,變數,保存我們所有引用類型的真實對象。 垃圾的產生:比如Test test = new Test();存放在棧中的test若一次調用之後很久不會用到,那麼該引用的值比如對應的年齡和姓名的值都存放在堆中,此時這些信息都會在堆中產生垃圾 堆記憶體中還要細分為三個區域: (1)新生區(伊甸園區) (2)養老區 (3)永久區
GC垃圾回收,主要是在伊甸園區和養老區,如果記憶體滿了,就會報堆記憶體不夠!如下圖:
在jdk8以後,永久存儲區改了個名字叫元空間
2.新生區
類:誕生和成長的地方,甚至死亡 所有對象都是在這裡new出來的,若此地方對象數量已經達到該區容量的最大值,則會觸發一次輕GC,也就是垃圾回收,在這過程中可能會有一些引用還會繼續使用,所以會存活下來,活下來的進程就會進入幸存者區(屬於新生區下的一個區),若幸存區也滿了又會進入養老區,若養老區還新生區都滿了就會觸發一次重GC2.永久區
這個區域常駐記憶體的,用來存放JDK自身攜帶的Class對象,Interface元數據,存儲的是Java運行時的一些環境或類信息,這個區域不存在垃圾回收!關閉VM虛擬就會釋放這個區域的記憶體。 在永久區蹦的情況:一個啟動類載入了大量的第三方jar包,Tomcat部署了太多的應用,大量動態生成的反射類,如果不斷的被載入,直到記憶體滿,就會出現OOM異常 jdk1.6之前:永久代,車行兩次是在方法區 jdk1.7:永久代,但是慢慢的退化了,去永久代,常量池在堆中 jdk1.8之後:無永久代,常量池在元空間點擊查看代碼
public static void main(String[] args) {
//返回虛擬機試圖使用的最大記憶體
long max = Runtime.getRuntime().maxMemory();
//返回jvm的初始化總記憶體
long total = Runtime.getRuntime().totalMemory();
System.out.println("max="+max+"位元組\t"+(max/(double)1024/1024)+"MB");
System.out.println("total="+total+"位元組\t"+(total/(double)1024/1024)+"MB");
//預設情況下:分配的總記憶體(最大記憶體)是電腦記憶體的1/4,而初始化的記憶體為1/64
}
運行結果圖
當遇到OOM(也就是記憶體溢出)時,解決方法如下:
(1)嘗試擴大記憶體,看是否還會繼續報錯
(2)若繼續報錯,分析記憶體,看一下哪個地方出現了問題(可能是死迴圈造成空間的無限占用)