JVM
認知
熟悉jvm架構以及垃圾回收機制以及相應的參數調優,有過在linux進行系統優化的經驗
線程和操作系統有關
過程
1)class文件通過類加載器加進內存區域---class loader類加載器
負責加載class文件,class文件在文件開頭有特定的文件標示,並且ClassLoader只負責class文件的加載,至於它是否可以運行,則由Execution Engine決定
2)Execution Engine執行引擎負責解釋命令,提交操作系統執行
3)Native Interface 本地接口
private native void start0();本地庫接口
java native interface JNI
調操作系統底層函數庫,調c的函數
本地接口的作用是融合不同的編程語言爲java所用,它的初衷是融合C/C++程序,java誕生的時候是C/C++橫行的時候,要想立足,必須調用C程序,於是就在內存中專門開闢了一塊區域處理標記爲native代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies.
目前該方法的使用越來越少了,除非是與硬件有關的應用,比如通過java程序驅動打印機,或者java系統管理生產設備,在企業級應用中已經比較少見
因爲現在的異構領域間的通信很發達,比如可以使用Socket通信,也可以使用web service等等,不多介紹
4)Runtime data area 運行數據區
Method Area方法區
方法區被所有線程共享,所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。簡單說,所有定義的方法的信息都保存在該區域,此區屬於共享區間。
靜態變量+常量+類信息+運行時常量池存在方法區中+實例變量存在堆內存中
PC Register 程序計數器
每個線程都有一個程序計數器,就是一個指針,指向方法區中的方法字節碼(下一個將要執行的指令代碼),由執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不計
Native Method Stack 本地方法棧
它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies
棧管運行,堆管存儲
棧也叫棧內存,主管java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來說不存在垃圾回收問題,只要線程一結束該棧就over,生命週期與線程一致,是線程私有的。基本類型的變亮和對象的引用變亮都是在函數的棧內存中國分配
每執行一個方法都會產生一個棧幀,保存到棧(後進先出)的頂部,頂部棧就是當前的方法,該方法執行完畢後會自動將此棧幀出棧
棧幀中主要保存3類數據
本地變量:輸入參數和輸出參數以及方法內的變亮
棧操作:記錄出棧,入棧的操作
棧幀數據:包括類文件,方法等等
判斷JVM優化在哪裏
方法區Method Area
堆Heap
三種JVM
Sun公司的HotSpot
BEA公司的JRockit
IBM公司的J9 VM
堆Heap
新生區
伊甸區(Eden Space)
new對象
JVM垃圾回收器將對伊甸園區進行垃圾回收(Minor GC)
大概內存使用70%的時候,銷燬九成以上的對象,剩下的進入倖存0區
倖存0區(Survivor 0 Space)
倖存1區(Survivor 1 Space)
養老區(Tenure Generation Space)
養老區用於保存從新生區篩選出來的java對象,一般池對象都在這個區域活躍
如果養老區滿了,那麼這個時候將產生Major GC(FullGC),進行養老區的內存清理
如果養老區執行了Full GC之後發現依然無法進行對象的保存,就會產生OOM異常“OutOfMemoryError”
永久存儲區(Permanent Space)
永久存儲區是一個常駐內存區域,用於存放JDK自身攜帶的Class,Interface的元數據,也就是說它存儲的是運行環境必須的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉JVM纔會釋放此區域所佔用的內存
如果出現java.lang.OutOfMemoryError:PermGen space,說明是java虛擬機對永久性代Perm內存設置不夠。一般這種情況,都是程序啓動需要加載大量的第三方jar包。
例如:在一個tomcat下部署了太多的應用。或者大量動態反射生成的類不斷被加載,最終導致Perm區被佔滿
jdk1.6及之前:有永久代,常量池在方法區
jdk1.7:有永久代,但已經逐步“去永久代”,常量池在堆
jdk1.8及之後:無永久代,常量池在元空間
如果出現java.lang.OutOfMemoryError:java heap sapce異常,說明java虛擬機的堆內存不夠。原因有二:
1、java虛擬機設置的堆內存設置不夠,可以通過參數-Xms、-Xmx來調整
2、代碼中創建了大量大對象,並且長時間不能被垃圾收集器收集(存在被引用)
JDK1.8
JDK1.8之後將最初的永久代取消了,由元空間取代
目的:將HotSpot與JRockit兩個虛擬機標準
JVM Heap
-Xms -Xmx
Young Gen
-Xmn
堆內存調優簡介
-Xms
設置初始分配大小,默認爲物理內存的“1/64”
-Xmx
最大分配內存,默認爲物理內存的“1/4”
-XX:+PrintCGDetails
輸出詳細的CG處理日誌
GC
是什麼
頻繁手機Young區
較少收集Old區
基本不動Perm區
四大算法
複製算法:MinorGC(普通GC)
新生代中使用的是Minor GC,這種GC算法採用的是複製算法(Copying)
Minor GC會把Eden中所有活的對象都移到Survivor區域中,如果Survivor區中放不下,那麼剩下的活的對象就被移到Oldgeneration中,也即一旦收集後,Eden就變成空的了
當對象在Eden(包括一個Survivor區域,這裏假設是from區域)出生後,在經過一次Minor GC後,如果對象還存活,並且能夠被另外一塊Survivor區域所容納(上面已經假設爲from區域,這裏應爲to區域,即to區域有足夠的內存空間來存儲Eden和From區域中存活的對象),則使用複製算法將這些仍然還存活的對象複製到另外一塊Survivor區域(即to區域)中,然後清理所使用過的Eden以及Survivor區域(即from區域),並且將這些對象的年齡設置爲1,以後對象在Survivor區每熬過一次Minor GC,就將對象的年齡+1,當對象的年齡達到某個值時(默認爲15歲,通過-XX:MaxTenuringThreshold來設定參數),這些對象就會成爲老年代
-XX:MaxTenuringThreshold
設置對象在新生代中存活的次數
複製算法不會產生內存碎片且對象完整不丟
複製算法彌補了標記/清除算法中,內存佈局混亂的缺點。
缺點
1、他浪費了一半的內存,這太要命了
2、如果對象存活率高,我們可以極端一點,假設是100%存活,那麼我們需要將所有對象都複製一遍,並將所有引用地址重置一遍。複製這一工作所花的時間,在對象存活率達到一定的程度時,將會變得不可忽視。所以從以上描述不難看出,複製算法要想使用,最起碼對象的存活率要非常低纔行,而且最重要的是,我們必須要克服50%內存的浪費
標記清除/標記整理算法:FullGC又叫MajorGC(全局GC)
老年代一般是由標記清除或者是標記清除與標記整理的混合實現
標記清除(Mark-Sweep)
1、標記(Mark)
從根集合開始掃描,對存活對象進行標記
2、清除(Sweep)
掃描整個內存空間,回收未被標記的對象,使用free-list記錄可以區域
優缺點
優點:不需要額外的空間
缺點:兩次掃描耗時嚴重,會產生內存碎片
標記整理(Mark-Compact)
標記---整理--清除:達到內存連續,不需要額外的空間,不產生內存碎片
劣勢
標記整理算法唯一的缺點就是效率也不高,不僅要標記所有存活對象,還要整理所有存活對象的引用地址。從效率上來說,標記整理算法要低於複製算法
引用計數法
已經被淘汰,解決不了雙端循環引用的問題
總結
內存效率
複製算法>標記清除>標記整理(此處的效率只是簡單的對比時間複雜度,實際情況不一定如此)
內存整齊度
複製算法=標記整理>標記清除
內存利用率
標記整理=標記清除>複製算法
分代收集算法
最合適的算法
就是根據JVM不同的代,量體裁衣