JVM內存區域組成 JVM內存分四種: 一、棧區(stacksegment)—由編譯器自動分配釋放,存放函數的參數值,局部變量的值等,具體方法執行結束以後,系統自動釋放JVM內存資源 二、堆區(heapsegment)—通常由程序員分配釋放,存放由new建立的對象和數組,jvm不定時查看這個對象,若是沒有引用指向這個對象就回收 三、靜態區(datasegment)—存放全局變量,靜態變量和字符串常量,不釋放 四、代碼區(codesegment)—存放程序中方法的二進制代碼,並且是多個對象共享一個代碼空間區域 在方法(代碼塊)中定義一個變量時,java就在棧中爲這個變量分配JVM內存空間,當超過變量的做用域後,java會自動釋放掉爲該變量所分配的JVM內存空間;在堆中分配的JVM內存由java虛擬機的自動垃圾回收器來管理,堆的優點是能夠動態分配JVM內存大小,生存期也沒必要事先告訴編譯器,由於它是在運行時動態分配JVM內存的。缺點就是要在運行時動態分配JVM內存,存取速度較慢;棧的優點是存取速度比堆要快,缺點是存在棧中的數據大小與生存期必須是肯定的無靈活性。 ◆java堆由Perm區和Heap區組成,Heap區則由Old區和New區組成,而New區又分爲Eden區,From區,To區,Heap={Old+NEW={Eden,From,To}},見圖1所示。 Heap區分兩大塊,一塊是NEWGeneration,另外一塊是OldGeneration.在NewGeneration中,有一個叫Eden的空間,主要是用來存放新生的對象,還有兩個SurvivorSpaces(from,to),它們用來存放每次垃圾回收後存活下來的對象。在OldGeneration中,主要存放應用程序中生命週期長的JVM內存對象,還有個PermanentGeneration,主要用來放JVM本身的反射對象,好比類對象和方法對象等。 在NewGeneration塊中,垃圾回收通常用Copying的算法,速度快。每次GC的時候,存活下來的對象首先由Eden拷貝到某個SurvivorSpace,當SurvivorSpace空間滿了後,剩下的live對象就被直接拷貝到OldGeneration中去。所以,每次GC後,EdenJVM內存塊會被清空。在OldGeneration塊中,垃圾回收通常用mark-compact的算法,速度慢些,但減小JVM內存要求. 垃圾回收分多級,0級爲所有(Full)的垃圾回收,會回收OLD段中的垃圾;1級或以上爲部分垃圾回收,只會回收NEW中的垃圾,JVM內存溢出一般發生於OLD段或Perm段垃圾回收後,仍然無JVM內存空間容納新的Java對象的狀況。 JVM調用GC的頻度仍是很高的,主要兩種狀況下進行垃圾回收:當應用程序線程空閒;另外一個是JVM內存堆不足時,會不斷調用GC,若連續回收都解決不了JVM內存堆不足的問題時,就會報outofmemory錯誤。由於這個異常根據系統運行環境決定,因此沒法預期它什麼時候出現。 根據GC的機制,程序的運行會引發系統運行環境的變化,增長GC的觸發機會。爲了不這些問題,程序的設計和編寫就應避免垃圾對象的JVM內存佔用和GC的開銷。顯示調用System.GC()只能建議JVM須要在JVM內存中對垃圾對象進行回收,但不是必須立刻回收,一個是並不能解決JVM內存資源耗空的局面,另外也會增長GC的消耗。 ◆當一個URL被訪問時,JVM內存區域申請過程以下: A.JVM會試圖爲相關Java對象在Eden中初始化一塊JVM內存區域 B.當Eden空間足夠時,JVM內存申請結束。不然到下一步 C.JVM試圖釋放在Eden中全部不活躍的對象(這屬於1或更高級的垃圾回收),釋放後若Eden空間仍然不足以放入新對象,則試圖將部分Eden中活躍對象放入Survivor區 D.Survivor區被用來做爲Eden及OLD的中間交換區域,當OLD區空間足夠時,Survivor區的對象會被移到Old區,不然會被保留在Survivor區 E.當OLD區空間不夠時,JVM會在OLD區進行徹底的垃圾收集(0級) F.徹底垃圾收集後,若Survivor及OLD區仍然沒法存放從Eden複製過來的部分對象,致使JVM沒法在Eden區爲新對象建立JVM內存區域,則出現"outofmemory錯誤"