Java虛擬機在執行Java程序的過程當中會把它所管理的內存劃分爲若干個不一樣的數據區域。根據《Java虛擬機規範(Java SE 7版)》的規定,Java虛擬機所管理的內存將會包括如下幾個運行時數據區域:java
描述的是Java方法執行的內存模型算法
Java虛擬機規範規定2種異常狀況:數組
永久代:HotSpot在1.7以前把GC分代收集擴展至方法區,即用永久代實現方法區緩存
若無,則執行類加載過程安全
同步分配內存空間2種方式:服務器
內存分配完成後,虛擬機須要將分配到的內存空間都初始化爲零值(不包括對象頭)數據結構
<init>
方法,初始化對象。HotSpot VM中,對象在內存中的佈局:多線程
對象頭(Header)架構
如下是Java程序經過棧上的Reference來操做堆上的具體對象。併發
方式一:使用句柄
方式二:使用直接指針
HotSpot使用這種
在HotSpot虛擬機中並不區分虛擬機棧和本地方法棧
不考慮虛擬機自己耗費內存、程序計數器內存(很小) 虛擬機棧和本地方法棧分配到的內存 = 進程內存 - 最大堆內存(Xmx)- 最大方法區(MaxPermSize) 因此線程數越多,單個線程內存就越小,成反比
-XX:MaxDirectMemorySize
進行設置,不設置則等同於Heap最大值。本章討論Heap內存的分配和回收
給對象中添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任什麼時候刻計數器爲0的對象就是不可能再被使用的。很難解決對象之間相互循環引用的問題
這個算法的基本思路就是經過一系列的稱爲"GC Roots"的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈(Reference Chain),當一個對象到GC Roots沒有任何引用鏈相連(用圖論的話來講,就是從GC Roots到這個對象不可達)時,則證實此對象是不可用的。
可做爲CG Root的對象:
Object() obj = new Object();
,只要存在引用,便沒法進行垃圾收集因此主動調用finalize()並不能當即觸發GC,它不是C++中的析構函數
永久代收集內容:
無用類:
不足:
安全點位置選定還需考慮GC時讓全部線程都進入此
在線程執行到Safe Region中的代碼時,首先標識本身已經進入了Safe Region,那樣,當在這段時間裏JVM要發起GC時,就不用管標識本身爲Safe Region狀態的線程了。在線程要離開Safe Region時,它要檢查系統是否已經完成了根節點枚舉(或者是整個GC過程),若是完成了,那線程就繼續執行,不然它就必須等待直到收到能夠安全離開Safe Region的信號爲止。
並行(Parallel):指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待狀態。併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不必定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另外一個CPU上。
關注的維度不一樣
吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。
缺點:
浮動垃圾:併發清除時用戶線程還在運行,可能在標記過程後產生部分垃圾,只能留到下次GC時清除。
G1的運做步驟:
jps[options][hostid] jps能夠經過RMI協議查詢開啓了RMI服務的遠程虛擬機進程狀態,hostid爲RMI註冊表中註冊的主機名
jstat[option vmid[interval[s|ms][count]]] interval:查詢間隔 count:次數 #每250毫秒查詢一次進程2764垃圾收集情況,一共查詢20次 jstat -gc 2764 250 20
jinfo[option]pid # 查詢CMSInitiatingOccupancyFraction參數值 $ jinfo -flag CMSInitiatingOccupancyFraction 13435 -XX:CMSInitiatingOccupancyFraction=-1
其餘方式得到dump文件:
jstack[option]vmid
在大多數網站形式的應用裏,主要對象的生存週期都應該是請求級或者頁面級的,會話級和全局級的長生命對象相對不多。只要代碼寫得合理,應當都能實如今超大堆中正常使用而沒有Full GC,這樣的話,使用超大堆內存時,網站響應速度纔會比較有保證。
垃圾收集進行時,虛擬機雖然會對Direct Memory進行回收,可是Direct Memory卻不能像新生代、老年代那樣,發現空間不足了就通知收集器進行垃圾回收,它只能等待老年代滿了後Full GC,而後「順便地」幫它清理掉內存的廢棄對象。不然它只能一直等到拋出內存溢出異常時,先catch掉,再在catch塊裏面「大喊」一聲:"System.gc()!"。要是虛擬機仍是不聽(譬如打開了-XX:+DisableExplicitGC開關),那就只能眼睜睜地看着堆中還有許多空閒內存,本身卻不得不拋出內存溢出異常了。而本案例中使用的CometD 1.1.1框架,正好有大量的NIO操做須要使用到Direct Memory內存。
從實踐經驗的角度出發,除了Java堆和永久代以外,咱們注意到下面這些區域還會佔用較多的內存,這裏全部的內存總和受到操做系統進程最大內存的限制。
Java的Runtime.getRuntime().exec()方法,首先克隆一個和當前虛擬機擁有同樣環境變量的進程,再用這個新的進程去執行外部命令,最後再退出這個進程。若是頻繁執行這個操做,系統的消耗會很大,不只是CPU,內存負擔也很重
虛擬機規範則是嚴格規定了有且只有5種狀況必須當即對類進行「初始化」(而加載、驗證、準備天然須要在此以前開始):
驗證的4個階段:
從Java開發人員角度能夠大體細分程3種:
啓動類加載器(Bootstrap ClassLoader)[不能直接使用]
擴展類加載器(Extension ClassLoader),[可直接使用]
應用程序類加載器(Application ClassLoader),[可直接使用]
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先判斷該類型是否已經被加載 Class c = findLoadedClass(name); if (c == null) { // 若是沒有被加載,就委託給父類加載或者委派給啓動類加載器加載 try { if (parent != null) { // 若是存在父類加載器,就委派給父類加載器加載 c = parent.loadClass(name, false); } else { // 若是不存在父類加載器,就檢查是不是由啓動類加載器加載的類,經過調用本地方法native Class findBootstrapClass(String name) c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 若是父類加載器和啓動類加載器都不能完成加載任務,才調用自身的加載功能 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
主流Java Web服務器要解決的問題:
Tomcat的目錄結構: