一、JVM內存分佈
1、JVM內存模型
JVM內存模型主要包括:類加載器,運行時數據區域,本地方法庫、本地方法接口,執行引擎和垃圾回收器
運行時數據區域主要包括:【線程獨享】:虛擬機棧、本地方法棧、程序計數器,【線程共享】:堆、方法區
2、類加載器
類加載器用於懶加載字節碼文件(.class文件),爲程序執行做準備
如果程序執行某個行爲所需要的類還沒有被加載到內存當中,則會經過三個步驟來完成類的初始化
- 加載:加載字節碼文件到內存當中
- 連接
- 驗證:字節碼文件是否有正確的結構
- 準備:分配類的一些信息到內存區域
- 解析:將類的二進制數據中的符號引用替換爲直接引用
- 初始化:創建對象的實例
JVM的類加載器分爲
- BootstrapClassLoader(啓動類加載器):加載JRE運行時類,位於$JAVA_HOME/jre/lib/rt.jar
- ExtClassLoader(擴展類加載器):加載JRE擴展類,位於$JAVA_HOME/jre/lib/ext目錄下
- AppClassLoader(應用程序類加載器):加載Java程序的類
- User ClassLoader(自定義加載器)
雙親委派模型:當類加載器加載類時,先嚐試由父類加載器加載,如果父類加載器無法完成,則再由子類加載器來完成
沙箱安全機制:由於雙親委派模型的存在,所以我們無法自己創建類似於JDK自帶的類(全類名完全相同)
雙親委派模型的破壞:Apache Tomcat是一個Web容器,用於部署Web應用,它就破壞了雙親委派模型,把類的加載直接交給Web應用類加載器(WebappClassLoader)來完成,如果Web應用類加載器(WebappClassLoader)無法完成,再由通用類加載器(CommonClassLoader)來完成。這樣做的目的主要是爲了保證每一個Web應用都是相互獨立的,不會因爲依賴着相同的類(全類名),而版本的不同造成混亂
Tomcat的類加載器分爲
- BootstrapClassLoader:加載基本的JVM環境的類,位於$JAVA_HOME/lib目錄下
- SystemClassLoader:加載Tomcat的啓動類,位於$CATALINA_HOME/bin目錄下
- CommonClassLoader:加載Tomcat的通用類,位於$CATALINA_HOME/lib目錄下
- WebappClassLoader:加載Web應用的類,位於WEB-INF/lib目錄和WEB-INF/classes目錄下。僅對當前Web應用可訪問
3、虛擬機棧
Java Stack,主要用於存放基本數據類型和實例對象的引用等
4、本地方法棧
Native Method Stack,和Java Stack類似,爲運行native方法而準備的
5、程序計數器
Program Counter Register,用於保存要執行的下一條指令
6、堆
Heap,主要用於存放對象的實例、數組和字符串常量池等
垃圾回收的主要區域,分爲新生代和老年代,默認比例爲1:2,可以通過修改JVM參數-XX:NewRatio=2
來調整
新生代又分爲伊甸園區、from區和to區,默認比例爲8:1:1。可以通過修改JVM參數-XX:SurvivorRatio=8
來調整
發生在新生代的GC稱之爲Minor GC,發生在老年代的GC稱之爲Major GC,發生在整個堆的GC稱之爲Full GC(包括對永久代的回收)
7、方法區
Method Area,主要用於存放類的信息(靜態變量、構造方法、方法聲明等)和運行時常量池(常量等)等
JVM規範將方法區描述爲堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的是和堆區分開來
在HotSpot虛擬機上,方法區又被稱之爲永久代(Permanent Generation),這僅僅是因爲HotSpot用永久代來實現方法區而已,在其他虛擬機上(BEA JRockit、IBM J9等)來說,是不存在永久代的概念的。但是這種實現做法容易遇到內存溢出的問題(OOM),可以通過修改JVM參數 [JDK7]-XX:PermSize=???
和-XX:MaxPermSize=???
[JDK8]-XX:MetaspaceSize=???
和-XX:MaxMetaspaceSize=???
來調整