一、記憶體處理流程
-
申請記憶體
? 通過配置引數或者默認的引數向作業系統申請記憶體,根據記憶體的大小找到記憶體段的起始地址和結束地址分配給JVM,由JVM進行內部分配,
-
初始化運行資料區
? 根據引數進行堆、方法區、堆疊的分配,
-
類加載(后面的文章會詳解)
? 將class、常量、靜態屬性放到方法區,物件放在堆中,
-
創建物件
? 創建執行緒,分配記憶體給虛擬機堆疊,虛擬機堆疊(運算元堆疊)創建物件,區域變數表存盤物件參考,物件放在堆中,
二、堆空間分代劃分
? 堆總被分為兩個部分:新生代和老年代,其中新生代中又被分為Eden區和Survivor區,Survivor區由Form Survivor和To Survivor組成(具體的GC、物件分配方面會在后面的文章講到),
三、堆疊優化
? 在堆疊幀中一般來說兩個堆疊幀是不會相互有關系的,都是獨立存在的,但是在某些情況下,會使兩個獨立的堆疊幀會有一個共享的資料區,此行為為堆疊的優化,
public class JVMTest01 {
public int add(int num){
return num + 2;
}
public static void main(String[] args) {
JVMTest01 jvmTest01 = new JVMTest01();
jvmTest01.add(50); //此時50就是add堆疊幀和main堆疊幀的共享資料
}
}

? 此時50就是main方法運算元堆疊的資料,同時也是add堆疊幀的區域變數表中的資料,此情況為堆疊優化,
三、JHSDB可視化JVM
后面有時間補上
四、記憶體溢位OOM
-
堆例外
? 記憶體溢位:申請的記憶體空間超過了堆最大的記憶體空間,
? 若為記憶體溢位,則可以通過
-Xms和-Xmx引數來調整堆記憶體的大小,如果不是就是代碼寫的有問題,物件的生命周期太長或者存盤結構上的設計不合理, -
方法區例外
? 運行時常量池溢位,
? 方法中的class物件占用的記憶體太多,超過了設定的記憶體大小,
-
堆疊例外
? 在Hotspot版本中的vm是不可以給堆疊設定大小,只能設定堆疊中的每個執行緒占用記憶體的大小,
? 每呼叫一個方法都會創建一個堆疊幀,所以一般來說出現的堆疊的oom基本上就是遞回的問題,
? 同時要注意,堆疊區的空間JVM 沒有辦法去限制的,因為JVM 在運行程序中會有執行緒不斷的運行,沒辦法限制,所以只限制單個虛擬機堆疊的大小,
-
直接記憶體例外
? 直接記憶體的容量可以通過
-XX:MaxDirectMemorySize來設定(默認與堆記憶體最大值一樣),所以也會出現OOM 例外;
? 由直接記憶體導致的記憶體溢位,一個比較明顯的特征是在HeapDump 檔案中不會看見有什么明顯的例外情況,如果發生了OOM,同時Dump 檔案很小,可以考慮重點排查下直接記憶體方面的原因,
五、常量池
-
class常量池(靜態常量池)
? 主要存放字面量和符號參考,
? 字面量:就是變數的值,例如:
String str = "Hello",'Hello'就是str的字面量,同理,int、double等等都是這樣,? 符號參考:就是在類被編譯的時候會被編譯成一個class位元組碼檔案,如果類中存在其他類的方法的在編譯的時候是不止到其他類的記憶體地址的,那么就用一個占位符(符號)來代替這個其他類,在類加載的時候就會把符號替換類的參考(記憶體地址),
? 例如:Student類中有呼叫Tool類中的一個方法,在編譯的時候就不知道這個Tool類的參考(記憶體地址),那么就會用com.xxx.Tool來代替,之后在類被加載的時候就會被替換成具體的參考(記憶體地址),
-
運行時常量池
? 在類被加載的時候會把符號轉換為具體的參考(記憶體地址)時,會把參考(記憶體地址)保存到這個區域,
? 在JDK1.7 被放在了堆中,但是理論中還是屬于方法區(JVM規范中),
? JDK1.8 之后由元空間來代替的永久代實作方法區,但是理論上還是隸屬于方法區,
-
字串常量池
? 字串常量池在網上是比較有爭議的一個存在,實際在官方檔案或者JVM規范中并沒有這個概念,
? 在JDK1.8中是存放在堆中的,且于Sting類有很大的關系,這塊記憶體的存在使Sting可以被更高效的使用,從而提升了的整體性能,
? String (JDK 1.8):

? 在String原始碼中可以看到String是被final所修飾的,它的底層為char陣列同時也是被final所修飾,因此可以知道String一旦被賦值后就不可被更改,那么這樣做的話會有什么好處呢?
? (1)保證了String物件的安全性,
? (2)保證它的hash值不會被頻繁變動,使得HashMap容器才能實作k-v的快取實作,
? (3)可以實作字串常量,
? String創建方式:
String str = "abc";
//此方法創建的話,首先會去常量池中查找是否存在abc這個常量,有的話就直接回傳參考,沒有就創建,
String str1 = new String("abc");
//這個方法比起上面,在堆中多了一個String物件,而這個String物件則指向常量池中的abc常量,所以str1首先指向的是堆中String物件而不是常量池中的abc,
public class Student {
String name;
String gender;
public static void main(String[] args) {
Student stu = new Student();
stu.setName("小明");
}
}
//此地的字符不會被存放在字串常量池中,只會被存在的堆中,
String str2 = "a" + "b" + "c";
//這個在目前的主流編譯器在被編譯的時候會被優化成 String str2 = "abc",以前則會生成 a、b、c、ab、abc這幾個物件,
String str1 = new String("abc").intern();
//在物件中呼叫intern方法,會和 String str1 = "abc" 等效,
?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/253407.html
標籤:其他

