JVM概述
JVM作用:
????????負責將字節碼翻譯為機器碼,管理運行時內存
JVM整體組成部分:
????????類加載系統(ClasLoader):負責將硬盤上的字節碼文件加載到內存中
????????運行時數據區(RuntimeData Area):負責存儲運行時各種數據
????????執行引擎(Execution Engine):負責將字節碼轉為機器碼
????????本地方法接口(Native Interface):負責調用本地方法(非Java的方法)
????????垃圾回收(重點)
類加載系統
作用
????????負責將硬盤上的字節碼文件加載到內存中(運行時數據區中)
類什么時候被加載
????????1.在一個類寫一個main方法,運行main方法
????????2.new某個類對象時
????????3.使用類中的靜態成員
????????4.使用反射機制時
public class Hello {final static int num = 10;static int num1= 10;/*靜態代碼塊在類被加載時自動執行,目前可以看做一個類只被加載一次*/static {System.out.println("類被加載了");}public static void main(String[] args) {System.out.println("111111111");}
}public class TestHello {public static void main(String[] args) throws ClassNotFoundException {//new Hello();//System.out.println(Hello.num);// Class.forName("com.ffyc.javapro.jvm.classloader.Hello");/*創建的是數組對象,數組是Hello類型*/Hello [] hellos = new Hello[10];//只是訪問類中的靜態常量,類是不加載的,直接返回靜態常量值即可System.out.println(Hello.num);//System.out.println(Hello.num1);}
}
類加載的過程(了解)
????????加載階段:以字節流形式讀取文件
????????連接階段:驗證? ?準備? ?解析
????????初始化階段:主要為靜態成員變量初始化賦值
類加載器
????????類加載器就是負責加載類的實踐者
????????不同的類,是由不同的類加載器加載的
類加載器的分類:
????????啟動類加載器(引導類加載器),不是用Java語言寫的,而是用C/C++寫的,負責加載虛擬機核心的類庫
????????擴展類加載器,是用Java語言寫的,負責加載jre/lib/ext目錄下的類
????????應用程序類加載器,是用Java語言寫的,負責加載程序員寫的項目中的類(target/class)
雙親委派機制
????????當收到類加載任務時,首先委派給上級的類加載器加載,如果上級類加載器還有父級,依次遞歸,直到最頂級的啟動類加載器,當父級類加載器找到類時,成功返回。
????????如果找不到,就要委派給子類加載器,如果子類加載器找到后,成功返回。
????????如果均未找到,那么就輸出ClassNotFoundException
為什么設計雙親委派機制
????????為了安全,避免了自己定義的類,替換了系統中的核心類
????????例如:自己創建java.lang.String,結果還是加載的系統中的String類
如何打破雙親委派機制
????????可以自定義類加載器
????????寫一個類? ?繼承ClassLoader類,
????????重寫findClass();
????????自己用流將字節碼讀入
Class<?> clazz = defineClass(null, bytes, 0, bytes.length);
Object o = clazz.newInstance();//反射機制創建對象//com.ffyc.javapro.jvm.classloader.MyClassLoader@1b6d3586
System.out.println(clazz.getClassLoader());
運行時數據區
當類加載系統把類信息加載到內存后,存儲到運行時數據區
運行時數據區,根據不同的功能可以分為5個部分:
程序計數器
作用:程序計數器用來記錄線程執行的指令集的位置,因為線程在執行時cpu要進行切換執行,需要記錄線程執行的位置
特點:
? ? ? ? 1.是運行時數據區中空間最小的,運行速度最快的區域
? ? ? ? 2.每個線程都有一個屬于自己的程序計數器,是線程私有的,程序計數器生命周期與線程生命周期相同
? ? ? ? 3.程序計數器是運行時數據區中唯一一個不會有內存異常情況的區域
虛擬機棧
虛擬棧是運行單位,管理程序如何執行,調用一個方法,方法入棧執行,運行結束后,出棧.
虛擬機棧主要用來運行java語言寫的方法.
特點:
????????線程私有的,每個線程中調用的方法都在線程對應的虛擬機棧中執行.
????????棧中存儲局部變量
????????虛擬棧中不存在垃圾回收
????????虛擬機棧中會存在內存溢出問題(遞歸調用太深)
????????Exception in thread "main" java.lang.StackOverflowError 棧溢出錯誤
????????A線程中的方法不能調用B線程中的方法
????????先進后出
public void test(){int a = 10;//局部變量int b= 20;String s = new String(); //s是引用類型,保存的是對象地址
}
當一個方法被調用后,被壓入到虛擬機棧中稱為一個棧幀,
棧幀內部結構:
? ? ? ??局部變量表(存儲局部變量的區域)
????????操作數棧(操作數棧就是用來計算的區域)
例如
int a= 10,int b=20; //a和b存儲在局部變量表中
int c = a+b ;//計算時,把a和b從局部變量表加載到操作數棧運算,把 運算結果賦給c,把c寫回到局部變量表
????????方法返回地址: 記錄方法調用的位置,方法執行完成后要回到自己開的位置
本地方法棧
本地方法: 在java程序中,不是用java語言實現的方法, 由底層操作系統提供
? ? ? ? ? ? ? ? ?使用 native關鍵修飾的方法,沒有方法體
因為java語言屬于上層語言(開發上層應用程序),沒有權限與底層硬件進行交互(如讀取內存數據,讀取硬盤數據),
本地方法棧用來執行本地方法的,當程序中調用了本地方法,那么被加載到本地方法棧中運行.
特點:
????????線程私有的,每個線程都有屬于自己的本地方法棧
????????本地方法棧也會出現內存溢出情況
????????本地方法棧中不會出現垃圾回收
堆
概述
作用: 堆空間是用來存儲java中創建的對象的
特點: 堆空間是運行時數據區中最大的一塊內存空間,
? ? ? ? ? 還可以根據需要通過參數設置大小: -Xms:10m(堆起始大小) -Xmx:30m(堆最大內大小
? ? ? ? ? 堆空間是所有線程共享的.
? ? ? ? ? 堆空間會出現內存溢出情況的.
? ? ? ? ? 堆空間是垃圾回收的重點區域.
堆內存區域劃分
新生代(區):
????????伊甸園區
????????幸存者0(from)
????????幸存者1(to)
?老年代(區):
為什么要分區(代)
根據對象的存活周期,對象的大小放在不同的區域,不同的區域可以采用不同的垃圾回收算法.
會頻繁的回收新生代, 相對較少回收老年代.
可以對回收算法揚長避短.
對象創建內存分配過程
1.新創建的對象都存儲在伊甸園區(比較大的對象,可以直接分配到老年代)
2.當下次垃圾回收到來時,把伊甸園區存活的對象移動到幸存者0區,清空伊甸園區
3.當下次垃圾回收時,把伊甸園區中存活的對象和幸存者0區的存活對象移動到幸存者1區,清空伊甸園區和幸存者0區.
4.當一個對象經歷過最大上限15次垃圾回收后,依然存活,那么將此對象移動到老年代
堆空間參數
涉及JVM調優面試題
根據實際的需要。來調整JVM中原有的一些參數,如堆的初始化大小,分代年齡
官網地址: https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
-XX:+PrintFlagsInitial查看所有參數的默認初始值
-Xms:初始堆空間內存
-Xmx:最大堆空間內存
-Xmn:設置新生代的大小
-XX:MaxTenuringTreshold:設置新生代垃圾的最大年齡
-XX:+PrintGCDetails 輸出詳細的 GC處理日志
方法區
概述
方法區主要存放類信息(屬性,方法,靜態常量...)和編譯器編譯后的代碼,
也是線程共享的區域,
方法的大小也是可以設置的,方法區的大小決定可以加載多少的類,
方法區也是有可能出現內存溢出的。
方法區大小設置
Java 方法區的大小不必是固定的,JVM可以根據應用的需要動態調整.
????????元數據區大小可以使用參數-XX:MetaspaceSize指定
方法區的垃圾回收
????????方法區也是有垃圾回收的,方法區的垃圾回收主要回收的是類信息。
????????類信息回收條件是比較苛刻的:
1.該類所創建的對象已經不再使用,并且被回收了
2.該類的Class對象也不再被使用了
3.加載該類的類加載器也被回收了
線程共享:堆,方法
線程私有的:程序計數器,虛擬機棧,本地方法棧
會出現內存溢出:堆,方法,虛擬機棧,本地方法棧
會出現垃圾回收:堆,方法區
本地方法接口
虛擬機中負責調用本地方法的入口,本地方法運行在本地方法棧中。
什么是本地方法
? ? ? ? 被native修飾的方法,沒有方法體,是操作系統提供的方法
為什么Java中要調用本地方法
? ? ? ? Java屬于上層應用開發語言,沒有權限直接訪問計算機硬件(硬盤,內存,外設(喇叭)),需要調用本地操作系統提供的方法。
執行引擎(黑盒)
執行引擎在虛擬機中主要負責將加載到虛擬機中的字節碼 解釋/翻譯 為機器碼
.java-----jdk編譯--->.class? ?在開發階段(前端編譯)
.class-----執行引擎編譯--->機器碼? ?在運行階段(后端編譯)
什么是解釋器?什么是JIT編譯器??
解釋器/解釋執行--->sql,html,css,js,python解釋執行? ?不需要整體編譯,由解釋器一行一行執行,
? ? ? ? 解釋執行特點:速度慢,不需要花費時間編譯
編譯執行,先把代碼整體進行編譯,生成另一種文件格式,
? ? ? ? 編譯執行特點:編譯后執行快,但是編譯需要花費一定的時間
jvm中的執行引擎在將字節碼編譯為機器碼時,采用半解釋,半編譯機制。
? ? ? ? 開始時,可以先采用解釋執行,立即投入到翻譯工作中,
? ? ? ? 等到編譯器編譯完成后,采用編譯執行
垃圾回收
Java語言特點
開源,跨平臺,面向對象,自動垃圾回收,線程,網絡...
概述
什么是垃圾?
一個對象沒有被任何引用指向,這個對象就可以被回收,就稱為垃圾對象,
垃圾對象如果不及時清理,導致新對象肯可能沒有空間存儲,進而導致內存溢出(內存不夠用了)
早期垃圾回收
早期是手動的回收 C和C++
????????malloc()
????????free()
給程序員帶來不便,如果忘記釋放,造成內存泄漏(對象不再使用,但是還占用著內存)
現在的語言多數采取了自動垃圾回收,例如java
程序員只需要new對象申請內存,不需要自己去釋放空間,解放了開發人員
應該關心哪些區域的回收?
重點是堆:頻繁回收新生代,較少回收老年代
方法區
內存溢出與內存泄漏
內存溢出:內存空間不足以運行程序,會報出內存錯誤(out of memory OOM)Error
內存泄漏:一些對象已經不再被使用l,但是垃圾回收器卻不能回收的對象,一直悄悄的占用著內存資源。
? ? ? ? 舉例:提供close()方法關閉資源的對象
? ? ? ? ? ? ? ? 數據庫連接對象,
? ? ? ? ? ? ? ? 網絡Socket
? ? ? ? ? ? ? ? IO讀取文件的對象
垃圾回收相關算法
垃圾標記階段算法
標記階段的目的:將堆內存中的對象進行檢查,檢查對象有沒有被引用指向。
標記階段涉及兩個算法:引用計數算法(現在的虛擬機已經不再使用了)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 可達性分析算法
引用計數算法:
? ? ? ? 思想:在每個對象中設置一個字段用來記錄引用的數量,有一個引用指向對象,計數器加一,一旦有引用斷開,計數器減一;
? ? ? ? 優缺點:實現思路簡單
? ? ? ? ? ? ? ? ? ? ? 計數器字段占用空間,加一,減一是需要開銷的,
? ? ? ? ? ? ? ? ? ? ? 重點是不能解決循環引用問題,A.B.C三個對象之間相互關聯引用,此時計數器都是1,但是可能與外界沒有聯系,外界不可能使用A.B.C這三個對象,垃圾回收器不能回收他們,造成內存泄漏問題。
????????????????????????Hello h1 = new Hello();? ? 引用計數器0
? ? ? ? ? ? ? ? ? ? ? ? Hello h2 = h1;
? ? ? ? ? ? ? ? ? ? ? ? h1 = null;
? ? ? ? ? ? ? ? ? ? ? ? h2 = null;
可達性分析算法(根搜索算法,追蹤性分析算法)
? ? ? ? 思想:從一些活躍對象開始進行搜索,只要跟根對象有聯系的對象,就不是垃圾對象,
? ? ? ? ? ? ? ? ? ?與根對象沒有任何聯系的,即使對象之間存在引用關系,也可以判定為垃圾對象。
? ? ? ? ? ? ? ? ? ?解決了引用計數算法中的循環引用問題。
? ? ? ? 哪些對象可以作為根對象:
? ? ? ? 1.虛擬機中引用的對象? ?運行中的方法中引用的對象
? ? ? ? 2.類中的一些靜態成員變量
? ? ? ? 3.與同步鎖有關的對象
? ? ? ? 4.虛擬機內部的一些類Class類,異常類,類加載器
final finally{ } finalize()
Object類中finalize()
public class Demo{protected void finalize() throws Throwable { Demo a = this;}
}
finalize()在對象被判定為垃圾后,在對象被真正回收之前由虛擬機自動調用,
在finalize()中執行一些最后要執行的功能,
finalize()只被執行一次
由于finalize()存在,對象可以分為3種狀態:
? ? ? ? 可觸及的:有引用指向的對象
? ? ? ? 可復活的:已經被判定為垃圾對象,但是fianlize()方法還沒執行過,有可能在finalize()中復活
? ? ? ? 不可觸及的:fianlize()已經被執行過了,并且又判定為垃圾了
垃圾回收階段算法
標記-復制算法
? ? ? ? 將內存分為兩塊,把正在使用中的內存塊的存活對象,復制到另一塊內存中,從內存塊的開始位置擺放,然后清除正在使用的內存塊.
????????對象會被移動, 適合存活對象少,垃圾對象多的場景, 適合新生代的回收.
標記-清除算法
????????不移動存活對象的,將垃圾對象中的地址記錄在一個空閑列表,有新對象到來時,可以把新對象分配到空閑列表中的內存地址上,覆蓋垃圾對象.
????????適合老年代回收,因為存活多且大,不需要移動對象.
標記-壓縮算法
????????將存活對象會壓縮到內存的一端,重新排列,將邊界外的空間進行清理,以減少內存碎片.
????????也是適用于老年代
垃圾回收器
標記階段算法和回收階段的算法都是方法論,垃圾回收器是真正回收的實踐者.
不同的jdk版本中提供不同的垃圾回收器, 不同的版本的jdk中可以由不同開發商實現垃圾回收器.
垃圾收集器分類
從線程數量分: 單線程垃圾收集器,只有一個線程進行垃圾回收
多線程垃圾收集器,有多個線程同時進行垃圾回收
從工作模式分: 獨占式垃圾收集器, 當垃圾收集線程執行時,其他程序線程會暫停執行
并發式垃圾收集器, 當垃圾收集線程執行時,可以允許其他程序線程同時執行
從回收的內存空間分: 新生代垃圾收集器
老年代垃圾收集器
jdk8中內置的垃圾回收器
Serial,Serial Old,
ParNew,Parallel Scavenge,Parallel Old,
CMS,
G1
重點了解2款垃圾收集器
CMS(Concurrent Mark Sweep,并發標記清除),這款垃圾收集器首創了垃圾回收線程和其他程序線程同時執行.
初始標記: 垃圾回收線程獨占的
并發標記: 用戶程序線程和垃圾回收線程同時執行的
重新標記: 垃圾回收線程獨占的
并發清理: 用戶程序線程和垃圾回收線程同時執行的
重置線程: 用戶程序線程和垃圾回收線程同時執行的