一個運行中的Java虛擬機有着一個清晰的任務:執行Java程序。程序開始執行時他才運行,程序結束時他就中止。你在同一臺機器上運行三個程序,就會有三個運行中的Java虛擬機。 Java虛擬機老是開始於一個main()方法,這個方法必須是公有、返回void、直接受一個字符串數組。在程序執行時,你必須給Java虛擬機指明這個包換main()方法的類名。main()方法是程序的起點,他被執行的線程初始化爲程序的初始線程。程序中其餘的線程都由他來啓動。html
Java中的線程分爲兩種:守護線程 (daemon)和普通線程(non-daemon)。守護線程是Java虛擬機本身使用的線程,好比負責垃圾收集的線程就是一個守護線程。固然,你也能夠把本身的程序設置爲守護線程。包含main()方法的初始線程不是守護線程。java
只要Java虛擬機中還有普通的線程在執行,Java虛擬機就不會中止。若是有足夠的權限,你能夠調用exit()方法終止程序。數組
1) 類裝載器(ClassLoader)(用來裝載.class文件)緩存
2) 執行引擎(執行字節碼,或者執行本地方法)tomcat
3) 運行時數據區(方法區、堆、java棧、PC寄存器、本地方法棧)安全
java堆是垃圾收集器管理的主要區域。java堆還能夠細分爲:新生代(New/Young)、舊生代/年老代(Old/Tenured)。持久代(Permanent)在方法區,不屬於Heap。服務器
新生代:新建的對象都由新生代分配內存。經常又被劃分爲Eden區和Survivor區。Eden空間不足時會把存活的對象轉移到Survivor。新生代的大小可由-Xmn控制,也可用-XX:SurvivorRatio控制Eden和Survivor的比例。併發
舊生代:存放通過屢次垃圾回收仍然存活的對象。jvm
持久代:存放靜態文件,現在Java類、方法等。持久代在方法區,對垃圾回收沒有顯著影響。函數
JVM棧是線程私有的,每一個線程建立的同時都會建立JVM棧,JVM棧中存放的爲當前線程中局部基本類型的變量、部分的返回結果以及Stack Frame。其餘引用類型的對象在JVM棧上僅存放變量名和指向堆上對象實例的首地址。
總結:Java對象實例存放在堆中;常量存放在方法區的常量池;虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據放在方法區;以上區域是全部線程共享的。棧是線程私有的,存放該方法的局部變量表(基本類型、對象引用)、操做數棧、動態連接、方法出口等信息。
一個Java程序對應一個JVM,一個方法(線程)對應一個Java棧。
Java代碼的編譯和執行包括了三個重要機制:
(1)Java源碼編譯機制(.java源代碼文件 -> .class字節碼文件)
(2)類加載機制(ClassLoader)
(3)類執行機制(JVM執行引擎)
4.1 Java源碼編譯機制
Java源代碼是不能被機器識別的,須要先通過編譯器編譯成JVM能夠執行的.class字節碼文件,再由解釋器解釋運行。即:Java源文件(.java) -- Java編譯器 --> Java字節碼文件 (.class) -- Java解釋器 --> 執行。流程圖以下:
字節碼文件(.class)是平臺無關的。
Java中字符只以一種形式存在:Unicode。字符轉換髮生在JVM和OS交界處(Reader/Writer)。
最後生成的class文件由如下部分組成:
4.2 類加載機制(ClassLoader)
Java程序並不一個可執行文件,是由多個獨立的類文件組成。這些類文件並不是一次性所有裝入內存,而是依據程序逐步載入。
JVM的類加載是經過ClassLoader及其子類來完成的,類的層次關係和加載順序能夠由下圖來描述:
(1)Bootstrap ClassLoader
(2)Extension ClassLoader
(3)App ClassLoader
(4)Custom ClassLoader
加載過程當中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視爲已加載此類,保證此類只全部ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。
雙親委派機制
JVM在加載類時默認採用的是雙親委派機制。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸。若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。
做用:1)避免重複加載;2)更安全。若是不是雙親委派,那麼用戶在本身的classpath編寫了一個java.lang.Object的類,那就沒法保證Object的惟一性。因此使用雙親委派,即便本身編寫了,可是永遠都不會被加載運行。
破壞雙親委派機制
雙親委派機制並非一種強制性的約束模型,而是Java設計者推薦給開發者的類加載器實現方式。
線程上下文類加載器,這個類加載器能夠經過java.lang.Thread類的setContextClassLoader()方法進行設置,若是建立線程時還未設置,它將會從父線程中繼承一個,若是在應用程序的全局範圍內都沒有設置過的話,那麼這個類加載器就是應用程序類加載器。像JDBC就是採用了這種方式。這種行爲就是逆向使用了加載器,違背了雙親委派模型的通常性原則。
4.3 類執行機制
Java字節碼的執行是由JVM執行引擎來完成,流程圖以下所示:
JVM是基於棧的體系結構來執行class字節碼的。線程建立後,都會產生程序計數器(PC)和棧(Stack),程序計數器存放下一條要執行的指令在方法內的偏移量,棧中存放一個個棧幀,每一個棧幀對應着每一個方法的每次調用,而棧幀又是有局部變量區和操做數棧兩部分組成,局部變量區用於存放方法中的局部變量和參數,操做數棧中用於存放方法執行過程當中產生的中間結果。
主要的執行技術:解釋,即時編譯,自適應優化、芯片級直接執行
開始對全部的代碼都採起解釋執行的方式,並監視代碼執行狀況。對那些常常調用的方法啓動一個後臺線程,將其編譯爲本地代碼,並進行優化。若方法再也不頻繁使用,則取消編譯過的代碼,仍對其進行解釋執行。
GC的基本原理:將內存中再也不被引用的對象進行回收,GC中用於回收的方法稱爲收集器。垃圾:再也不被引用的對象。
因爲GC須要消耗一些資源和時間,Java在對對象的生命週期特徵進行分析後,按照新生代、舊生代的方式來對對象進行收集,以儘量的縮短GC對應用形成的暫停。
Java垃圾回收是單獨的後臺線程gc執行的,自動運行無需顯示調用。即便主動調用了java.lang.System.gc(),該方法也只會提醒系統進行垃圾回收,但系統不必定會迴應,可能會不予理睬。
判斷一塊內存空間是否符合回收標準:
(1)對象賦予了空值,且以後再未調用(obj = null;)
(2)對象賦予了新值,即從新分配了內存空間(obj = new Obj();)
內存泄漏:程序中保留着對永遠再也不使用的對象的引用。所以這些對象不回被GC回收,卻一直佔用內存空間卻毫無用處。即:1)對象是可達的;2)對象是無用的。知足這兩個條件便可斷定爲內存泄漏。
應確保不須要的對象不可達,一般採用將對象字段設置爲null的方式,或從容器collection中移除對象。局部變量再也不使用時無需顯示設置爲null,由於對局部變量的引用會隨着方法的退出而自動清除。
內存泄露的緣由:1)全局集合;2)緩存;3)ClassLoader
調優目的:減小GC的頻率尤爲是Full GC的次數,過多的GC會佔用不少系統資源影響吞吐量。特別要關注Full GC,由於它會對整個堆進行整理。
主要手段:JVM調優主要經過配置JVM的參數來提升垃圾回收的速度,合理分配堆內存各部分的比例。
致使Full GC的幾種狀況和調優策略:
堆內存比例不良設置會致使什麼後果:
1)新生代設置太小
一是新生代GC次數很是頻繁,增大系統消耗;二是致使大對象直接進入舊生代,佔據了舊生代剩餘空間,誘發Full GC
2)新生代設置過大
一是新生代設置過大會致使舊生代太小(堆總量必定),從而誘發Full GC;二是新生代GC耗時大幅度增長
通常說來新生代佔整個堆1/3比較合適
3)Survivor設置太小
致使對象從eden直接到達舊生代,下降了在新生代的存活時間
4)Survivor設置過大
致使eden太小,增長了GC頻率
另外,經過-XX:MaxTenuringThreshold=n來控制新生代存活時間,儘可能讓對象在新生代被回收
JVM提供兩種較爲簡單的GC策略的設置方式:
1)吞吐量優先
JVM以吞吐量爲指標,自行選擇相應的GC策略及控制新生代與舊生代的大小比例,來達到吞吐量指標。這個值可由-XX:GCTimeRatio=n來設置
2)暫停時間優先
JVM以暫停時間爲指標,自行選擇相應的GC策略及控制新生代與舊生代的大小比例,儘可能保證每次GC形成的應用中止時間都在指定的數值範圍內完成。這個值可由-XX:MaxGCPauseRatio=n來設置
JVM常見配置
參考連接:
http://www.open-open.com/lib/view/open1408453806147.html
http://www.codeceo.com/article/jvm-memory-overflow.html
http://blog.csdn.net/cutesource/article/details/5907418