一、JVM 位置二、JVM 體系結構概覽三、堆體系結構概述四、堆參數調優入門五、JVM 的配置和優化六、Tomcat 的配置和優化php
熟悉 JVM 架構與 GC 垃圾回收機制以及相應的 JVM 調優,有過在 Linux 系統下的調優經驗。java
淘寶的周志明《深刻理解 Java 虛擬機》中說 JVM 的優化,其中 99% 優化的是堆,1% 優化的是方法區。面試
內地女歌手照片--李嘉欣,貼在桌面上。算法
JVM 是運行在操做系統之上的,它與硬件沒有直接的交互編程
詳解以下:tomcat
類裝載器 Class Loader
負責加載 class 文件,class 文件在文件開頭有特定的文件標示,而且 Class Loader 只負責 class 文件的加載,至於它是否能夠運行,則由 Execution Engine 決定。架構
虛擬機自帶的加載器
啓動類加載器(Bootstrap)C++
擴展類加載器(Extension)Java
應用程序類加載器(AppClassLoader)Java,也叫系統類加載器,加載當前應用的 classpath 的全部類app
用戶自定義的加載器
Java.lang.ClassLoader 的子類,用戶能夠定製類的加載方式。
Code 案例
sun.misc.Launcher 它是一個 java 虛擬機的入口應用。eclipse
Execution Engine 執行引擎
執行引擎負責解釋命令,提交操做系統執行。jvm
Native Interface 本地接口
本地接口的做用是融合不一樣的編程語言爲 Java 所用,它的初衷是融合 C/C++ 程序,Java 誕生的時候是 C/C++ 橫行的時候,要想立足,必須有調用 C/C++ 程序,因而就在內存中專門開闢了一塊區域處理標記爲 native 的代碼,它的具體作法是 Native Method Stack 中登記 native 方法,在 Execution Engine 執行時加載 native libraies。
目前該方法使用的愈來愈少了,除非是與硬件有關的應用,好比經過 Java 程序驅動打印機或者 Java 系統管理生產設備,在企業級應用中已經比較少見。
由於如今的異構領域間的通訊很發達,好比可使用 Socket 通訊,也可使用 Web Service 等等,很少作介紹。
Native Method Stack 本地方法棧
它的具體作法是 Native Method Stack 中登記 native 方法,在 Execution Engine 執行時加載本地方法庫。
PC Register 程序寄存器
每一個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區中的方法字節碼(用來存儲指向下一條指令的地址,也即將要執行的指令代碼),由執行引擎讀取下一條指令,是一個很是小的內存空間,幾乎能夠忽略不記。
這塊內存區域很小,它是當前線程所執行的字節碼的行號指示器,字節碼解釋器經過改變這個計數器的值來選取下一條須要執行的字節碼指令。
若是執行的是一個 Native 方法,那這個計數器是空的。
Method Area 方法區
方法區是被全部線程共享,全部字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。簡單說,全部定義的方法的信息都保存在該區域,此區屬於共享區間
。
靜態變量 + 常量 + 類信息(構造方法/接口定義) + 運行時常量池 存在方法區中
,可是實例變量存在堆內存中,和方法區無關
。
方法區主要存放的是:構造方法 + 接口的代碼
Stack 棧是什麼
棧也叫棧內存,主管 Java 程序的運行,是在線程建立時建立,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來講不存在垃圾回收問題
,只要線程一結束該棧就 over,生命週期和線程一致,是線程私有的。8 種基本類型的變量 + 對象的引用變量 + 實例方法
都是在函數的棧內存中分配。
棧存儲什麼?
棧幀中主要保存 3 類數據:
本地變量(Local Variables):輸入參數和輸出參數以及方法內的變量。
棧操做(Operand Stack):記錄出棧、入棧的操做。
棧幀數據(Frame Data):包括類文件、方法等等。
棧運行原理
棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法(Method)和運行期數據的數據集,當一個方法 A 被調用時就產生了一個棧幀 F1,並被壓入到棧中,A 方法又調用了 B 方法,因而產生棧幀 F2 也被壓入棧,B 方法又調用了 C 方法,因而產生棧幀 F3 也被壓入棧,…… 執行完畢後,先彈出 F3 棧幀,再彈出 F2 棧幀,再彈出 F1 棧幀,……
遵循 「先進後出」/「後進先出」 原則。
每一個方法執行的同時都會建立一個棧幀,用於存儲局部變量表、操做數棧、動態連接、方法出口
等信息,每個方法從調用直至執行完畢的過程,就對應着一個棧幀在虛擬機中入棧到出棧的過程。棧的大小和具體 JVM 的實現有關,一般在 256K~756K 之間。
棧內存溢出異常:Exception in thread "main" java.lang.StackOverflowError
棧管運行,堆管存儲。
三種 JVM
一、Sun 公司的 HotSpot(Oracle 收購)
二、BEA 公司的 JRockit(Oracle 收購)
三、IBM 公司的 J9 VM
JVM 優化的是哪裏?
Heap 堆
一個 JVM 實例只存在一個堆內存,堆內存的大小是能夠調節的。類加載器讀取了類文件後,須要把類、方法、常變量放到堆內存中,保存全部引用類型的真實信息,以方便執行器執行,堆內存分爲三部分:
Heap 堆(Java7 以前)
一個 JVM 實例只存在一個堆內存,堆內存的大小是能夠調節的。類加載器讀取了類文件後,須要把類、方法、常變量放到堆內存中,保存全部引用類型的真實信息,以方便執行器執行。
堆內存邏輯上分爲三部分:新生 + 養老 + 永久
新生區
新生區是類的誕生、成長、消亡的區域,一個類在這裏產生、應用、最後被垃圾回收器收集、結束生命。新生區又分爲兩部分:伊甸區(Eden space)和倖存者區(Survivor pace),全部的類都是在伊甸區被 new 出來的。倖存區有兩個:0 區(Survivor 0 space)和 1 區(Survivor 1 space)。當伊甸園的空間用完時,程序又須要建立對象,JVM 的垃圾回收器將對伊甸園區進行垃圾回收
(Minor GC
),將伊甸園區中的再也不被其餘對象所引用的對象進行銷燬
。而後將伊甸園中的剩餘對象移動到倖存 0 區。若倖存 0 區也滿了,再對該區進行垃圾回收,而後移動到 1 區。那若是
1 區也滿了呢?再移動到養老區。若養老區也滿了,那麼這個時候將產生 Major GC
(Full GC
),進行養老區的內存清理。若養老區執行了 Full GC 以後發現依然沒法進行對象的保存,就會產生 OOM 異常 「OutOfMemoryError」。
若是出現 java.lang.OutOfMemoryError: Java heap space
異常,說明 Java 虛擬機的堆內存不夠。緣由有二:
(1)Java 虛擬機的堆內存設置不夠,能夠經過參數 -Xms、-Xmx 來調整
。
(2)代碼中建立了大量大對象,而且長時間不能被垃圾收集器收集(存在被引用)
。
如何 new 一個大對象? 答:byte[] byteArray = new byte[1 * 1024 * 1024 * 7000];
養老區
養老區用於保存重新生區篩選出來的 Java 對象,通常池對象都在這個區域活躍。
永久區
永久存儲區是一個常駐內存區域,用於存放 JDK 自身所攜帶的 Class、Interface 的元數據,也就是說它存儲的是運行環境必須的類信息
,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉 JVM 纔會釋放此區域所佔用的內存。
若是出現 java.lang.OutOfMemoryError: PermGen space
,說明是 Java 虛擬機對永久代 Perm 內存設置不夠。通常出現這種狀況,都是程序啓動須要加載大量的第三方 jar 包。例如:在一個 Tomcat 下部署了太多的應用。或者大量動態反射生成的類不斷被加載,最終致使 Perm 區被佔滿。
Java 7 叫永久代,Java 8 叫元空間。
實際而言,方法區(Method Area)和堆同樣,是各個線程共享的內存區域,它用於存儲虛擬機加載的:類信息+普一般量+靜態常量+編譯器編譯後的代碼等等,雖然 JVM 規範將方法區描述爲堆的一個邏輯部分,但它卻還有一個別名叫作 Non-Heap(非堆),目的就是要和堆分開。
對於 HotSpot 虛擬機,不少開發者習慣將方法區稱之爲「永久代(Parmanent Gen)」,但嚴格本質上說二者不一樣,或者說使用永久代來實現方法區而已,永久代是方法區(至關因而一個接口 interface)的一個實現
,jdk1.7 的版本中,已經將本來放在永久代的字符串常量池移走
。
jdk 1.6 方法區就是永久代。常量池在方法區中。
jdk 1.7 中 常量池放在了堆中。
RUNTIME CONSTANT POOL 運行時常量池
常量池(Constant Pool)是方法區的一部分,Class 文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,常量池用於存放編譯期間生成的各類字面量和符號引用,這部份內容將在類加載後進入方法區的運行時常量池中存放。
熟悉三區結構後方可學習-JVM 垃圾收集
JVM 垃圾收集(Java Garbage Collection)
以 JDK1.7 + HotSpot 爲例
以 JDK1.8 + HotSpot 爲例
堆內存調優簡介 01
堆內存調優簡介 02
發現默認的狀況下分配的內存是總內存的「1 / 4」、而初始化的內存爲「1 / 64」
堆內存調優簡介 03
java 7
堆內存調優簡介 04
調整 VM 參數並打印出來:-Xms8m -Xmx8m -XX:+PrintGCDetails
java 7
MAT(Eclipse Memory Analyzer Tool)
安裝方式1:離線 jar 包方式
1)Eclipse Memory Analyzer Windows 64 位下載地址:http://www.eclipse.org/downloads/download.php?file=/mat/1.8.1/rcp/MemoryAnalyzer-1.8.1.20180910-win32.win32.x86_64.zip
2)解壓下載包:放到 eclipse 或 myeclipse 安裝目錄的 dropins 目錄下。
3)啓動 eclipse 或 myeclipse,打開 Window - > Perspective,看到 Memory Analyzer 證實安裝成功。
安裝方式2:聯網插件安裝方式
http://download.eclipse.org/mat/1.8.1/update-site/
使用 MAT 分析
啓動 eclipse 或 myeclipse,打開 File - > Open heap dump,在彈出的對話框選擇生成的 dump文件,就能夠看到 MAT 給出了overview page。
面試題
JVM 複習
GC 是什麼
頻繁收集 新生區,使用的算法是複製算法
較少收集 老年區,使用的算法是整理-壓縮算法
基本不動 永久區 (jdk7)
GC 三大算法
GC 算法整體概述
複製算法:Minor GC(普通 GC)
新生代中使用的是 Minor GC(普通 GC),這種 GC 算法採用的是複製算法(Copying)
複製算法的原理以下圖所示:
Minor GC(普通 GC)會把 Eden 中的全部活的對象都移到 Survivor 區域中,若是 Survivor 區域中放不下,那麼剩下的活的對象就被移動到 Old Generation 中,也即一旦收集後,Eden 就變成空的了
。
複製算法的缺點:
標記清除算法/標記整理算法:Full GC 又叫 Major GC(全局 GC)
老年代通常是由標記清除或者是標記清除與標記整理的混合實現。
標記清除:Mark-Sweep
標記清除原理
標記清除算法的缺點:
標記整理:Mark-Compact
標記整理算法原理
標記整理算法的缺點:
小總結
面試題