Java虛擬機
JVM
java 虛擬機 jvm 是 java 實現跨平台的重要部分,jvm 是虛擬化的計算機,有完整的硬件功能,可以屏蔽底層不同的操作系統,只需要計算機廠商在不同系統中安裝虛擬機,定義好如何將字節碼文件解析成當前計算機系統識別的計算機碼即可。從而實現JAVA的跨平台。JVM中重要的2個部分別是 JAVA內存模型和GC。大家要搞清楚JVM 是內存中的虛擬機,即JVM的存儲就是內存
JVM 架構

- classLoader: 按照特特定格式,加載class文件到內存。
- Runtime Date Area: JVM 內存模型,JMM。
- Execution Engine: 對命令進行解析。class 文件是按照特定格式書寫的,只有可以被Execution Engine 解析的class文件才是合法的。
- Native Interface: 本地接口,主要是用來加載第三方API 比如C++等,融合不同的語言被JAVA所用,如 Class.forName 方法 底層就是Native 接口
JAVA 反射機制
JAVA 反射是在程序運行過程中,對任意的類或者是對象,我們能夠知道或者調用對象的任意方法和屬性,這種動態獲取信息以及動態調用方法的機制就是JAVA語音的反射。
public class Robot {
private String name;
public void sayHi(String hello){
System.out.println("hello:"+hello);
}
private String throwHello(String hi){
System.out.println("Hi:"+hi);
return hi;
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class roClass = Class.forName("com.java.imooc.reflect.Robot");
Robot robot = (Robot) roClass.newInstance();
System.out.println(roClass.getName());
// 不能獲取私有方法,可以獲取public 和 繼承 實現的方法
Method[] methods = roClass.getMethods();
//String.class 是因為 throwHello 接收一個 String 對象,不能獲取的是繼承的方法和接口的方法,獲取該類所有的方法。
Method throwHello = roClass.getDeclaredMethod("throwHello", String.class);
// 私有方法 需要設置為true
throwHello.setAccessible(true);
Object bobo = throwHello.invoke(robot, "bobo");
System.out.println(bobo);
System.out.println(methods);
}
JAVA 從編譯到執行的過程
- 編譯器將.java的文件編譯成.class文件
- classLoader 將字節碼文件轉轉換為JVM中的Class對象
- JVM利用Class對象實例化 T對象
ClassLoader
ClassLoader 在java中有着非常重要的作用,主要工作在class 裝載的加載階段,主要功能是將外部的class 二進制數據流,裝載進系統,然后交給JAVA虛擬機進行連接,初始化等操作。
ClassLoader 的種類
- BootStrapClassLoader: C++ 編寫,加載核心庫 java.*
- ExtClassLoader: JAVA 編寫 ,加載擴展庫 javax.
- AppClassLoader: JAVA 編寫,加載程序所在目錄
- 自定義ClassLoader: JAVA編寫,用戶自定義。
ClassLoader 雙親委派機制
雙親委派機制 原則是先判斷后加載,先判斷Custom ClassLoader 是否加載過,如果沒有判斷父加載器AppClassLoader 是否加載過,如果沒有繼續判斷Extension ClassLoader 是否加載過,
如果沒有繼續判斷 BootStrap ClassLoader 是否加載過,如果都沒有加載過,那么按照反向 進行加載,即由BootStrap ClassLoader 進行加載,如果BootStrap ClassLoader 庫里沒有,那么繼續
向下 由Extension ClassLoader 進行加載,如果庫里也沒有,那么繼續由子類 AppClassLoader 進行加載,如果在對應的jar里沒有找到,那么最后由 Custom ClassLoader 進行加載,都沒有 則拋出異常
判斷是否加載 由下向上進行一次判斷,加載過程是由上向下進行一次加載,這個判斷加載的機制被稱為雙親委派機制
- 采用這個機制的目的是防止同一份字節碼被多次加載
代碼閱讀
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
類的加載方式
- 隱式加載: new
- 顯示加載:loadClass,forName等
loadClass,forName 區別

- Class.forName 得到的class 是已經初始化完成的
- Classloader.loadClass 得到的class 是還沒有鏈接的,說明只完成第一步。
JAVA內存模型

-
線程私有:程序計數器,虛擬機棧,本地方法棧
-
線程共享:MetaSpace,Java 堆
-
程序計數器
-
1. 當前線程所執行的字節碼行號指示器。 2. 改變計數器的值來選取下一條需要執行的字節碼指令 3. 和現成是一對一的關系,即線程私有 4. 對Java 方法計數,如果是Native方法則計數器值為Undefined 5. 不會發生內存泄露 -
Java 虛擬機棧(Stack)
-
1. Java 方法的的內存模型 2. 包含多個棧楨 -
元空間(MetaSpace)和 永久代(PermGen)
- jdk8 以后 元空間替代永久代,元空間使用本地內存,永久代使用的是jvm 內存。
- 替代好處是 字符串在永久代中容易出現性能問題和內存溢出問題。
- 類和方法的信息大小難以確認,給永久代的大小指定帶來康困難。
- 永久代會給GC帶來不必要的復雜性
-
Java 堆
- 對象實例的分配區域
- 是GC管理的主要區域
JVM 三大性能調優參數 -Xms -Xmx -Xss 的含義
- Xss :每個虛擬機棧(堆棧)的大小 一般情況下 256k 足夠了 ,此配置回影響此進程中並發線程數量的大小
- Xms:堆的初始值,以及該線程剛創建出來的時候java堆的大小,一旦對象容量大於java堆容量,java堆會自動擴容。擴容到Xmx的大小
- Xmx:java堆能擴展到的最大值
一版情況下我們會將Xms和Xmx設置成一樣的 因為在擴容的時候會發生內存抖動,影響程序運行的穩定性
Java內存中堆和棧的區別----內存分配策略
- 靜態存儲:在編譯時確定每一個數據目標在運行時的存儲空間需求
- 棧式存儲:數據區需求在編譯時未知,運行時模塊入口前確定
- 堆式存儲:編譯時或運行時模塊入口未知,無法確定,動態分配。
- 聯系: 引用對象,數組時,棧里定義變量保存堆中目標的首地址

- 管理方式:棧自動施放,堆需要GC回收
- 空間帶下:棧比堆小
- 碎片相關:棧的碎片比堆的碎片少
- 分配方式:棧支持靜態和動態分配,而堆只支持動態分配
- 效率: 棧比堆高
