1.JVM內存管理的機制
內存空間劃分為:Sun JDK在實現時遵照JVM規范,將內存空間劃分為堆、JVM方法棧、方法區、本地方法棧、PC寄存器。
- 堆:堆用于存儲對象實例及數組值,可以認為Java中所有通過new創建的對象的內存都在此分配,Heap中對象所占用的內存由GC進行回收,在32位操作系統上最大為2GB,在64位操作系統上則沒有限制,其大小可通過-Xms和-Xmx來控制,-Xms為JVM啟動時申請的最小Heap內存,默認為物理內存的1/64但小于1GB;-Xmx為JVM可申請的最大Heap內存,默認為物理內存的1/4但小于1GB,默認當空余堆內存小于40%時,JVM會增大Heap到-Xmx指定的大小,可通過-XX:MinHeapFreeRatio=來指定這個比例;當空余堆內存大于70%時,JVM會減小Heap的大小到-Xms指定的大小,可通過-XX:MaxHeapFreeRatio=來指定這個比例,對于運行系統而言,為避免在運行時頻繁調整Heap 的大小,通常將-Xms和-Xmx的值設成一樣。
- JVM方法棧:為線程私有,其在內存分配上非常高效。當方法運行完畢時,其對應的棧幀所占用的內存也會自動釋放。當JVM方法棧空間不足時,會拋出StackOverflowError的錯誤,在Sun JDK中可以通過-Xss來指定其大小。
- 方法區:要加載的類的信息(名稱、修飾符等)、類中的靜態變量、類中定義為final類型的常量、類中的Field信息、類中的方法信息。方法區域也是全局共享的,在一定條件下它也會被GC,當方法區域要使用的內存超過其允許的大小時,會拋出OutOfMemory的錯誤信息。在Sun JDK中這塊區域對應Permanet Generation,又稱為持久代,默認最小值為16MB,最大值為64MB,可通過-XX:PermSize及-XX:MaxPermSize來指定最小值和最大值。
- 本地方法棧:用于支持native方法的執行,存儲了每個native方法調用的狀態。在Sun JDK的實現中,和JVM方法棧是同一個。
- PC寄存器:占用的可能為CPU寄存器或操作系統內存。
2.Java堆和棧的區別
Java把內存劃分成兩種:一種是棧內存,一種是堆內存。
在函數中定義的一些基本類型的變量和對象的引用變量都在函數的棧內存中分配。當在一段代碼塊定義一個變量時,Java就在棧中為這個變量分配內存空間,當超過變量的作用域后,Java會自動釋放掉為該變量所分配的內存空間,該內存空間可以立即被另作他用。
堆內存用來存放由new創建的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。在堆中產生了一個數組或對象后,還可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等于數組或對象在堆內存中的首地址,在棧中的這個特殊的變量就變成了數組或者對象的引用變量,以后就可以在程序中使用棧內存中的引用變量來訪問堆中的數組或者對象,引用變量相當于為數組或者對象起的一個別名,或者代號。
引用變量是普通變量,定義時在棧中分配內存,引用變量在程序運行到作用域外釋放。而數組&對象本身在堆中分配,即使程序運行到使用new產生數組和對象的語句所在地代碼塊之外,數組和對象本身占用的堆內存也不會被釋放,數組和對象在沒有引用變量指向它的時候,才變成垃圾,不能再被使用,但是仍然占著內存,在隨后的一個不確定的時間被垃圾回收器釋放掉。這個也是java比較占內存的主要原因。但是在寫程序的時候,可以人為的控制。
3.Java內存泄露和內存溢出
內存泄漏:分配出去的內存回收不了
內存溢出:指系統內存不夠用了
4.Java類加載機制
JVM將類加載過程劃分為三個步驟:裝載、鏈接和初始化。
- 裝載(Load):裝載過程負責找到二進制字節碼并加載至JVM中,JVM通過類的全限定名(com.bluedavy. HelloWorld)及類加載器(ClassLoaderA實例)完成類的加載;
- 鏈接(Link):鏈接過程負責對二進制字節碼的格式進行校驗、初始化裝載類中的靜態變量及解析類中調用的接口、類;
- 初始化(Initialize):執行類中的靜態初始化代碼、構造器代碼及靜態屬性的初始化。
5.內存回收
收集器:引用計數收集器、跟蹤收集器
- 引用計數收集器:對于Java這種面向對象的會形成復雜引用關系(如ObjectB和ObjectC互相引用)的語言而言,引用計數收集器不是非常適合,Sun JDK在實現GC時也未采用這種方式。
- 跟蹤收集器實現算法:復制(Copying)、標記-清除(Mark-Sweep)和標記-壓縮(Mark-Compact)
復制:當要回收的空間中存活對象較少時,復制算法會比較高效,其帶來的成本是要增加一塊空的內存空間及進行對象的移動。
標記-清除:在空間中存活對象較多的情況下較為高效,但由于標記-清除采用的為直接回收不存活對象所占用的內存,因此會造成內存碎片。
標記-壓縮:在標記-清除的基礎上還須進行對象的移動,成本相對更高,好處則是不產生內存碎片。