天天看點

java中的記憶體管理

Java堆

堆記憶體用來存放由new建立的對象執行個體和數組。(重點)

 Java堆是所有線程共享的一塊記憶體區域,在虛拟機啟動時建立,此記憶體區域的唯一目的就是存放對象執行個體 。

 Java堆是垃圾收集器管理的主要區域。由于現在收集器基本采用分代回收算法,是以Java堆還可細分為:新生代和老年代。從記憶體配置設定的角度來看,線程共享的Java堆中可能劃分出多個線程私有的配置設定緩沖區(TLAB)。

 Java堆可以處于實體上不連續的記憶體空間,隻要邏輯上連續的即可。在實作上,既可以實作固定大小的,也可以是擴充的。

 如果堆中沒有記憶體完成執行個體配置設定,并且堆也無法完成擴充時,将會抛出OutOfMemoryError異常。

Java棧

  在棧記憶體中儲存的是堆記憶體空間的通路位址,或者說棧中的變量指向堆記憶體中的變量(Java中的指針)(重點)。

 Java棧是Java方法執行的記憶體模型每個方法在執行的同時都會建立一個棧幀的用于存儲局部變量表、操作數棧、動态連結、方法出口等資訊。每個方法從調用直至執行完成的過程就對應着一個棧幀在虛拟機中入棧和出棧的過程。

堆和棧的聯系

 當在堆中産生了一個數組或者對象時,可以在棧中定義一個特殊的變量,讓棧中的這個變量的取值等于數組或對象在堆記憶體中的首位址,棧中的這個變量就成了數組或對象的引用變量,以後就可以在程式中使用棧中的引用變量來通路堆中的數組或者對象,引用變量就相當于是為數組或者對象起的一個名稱。引用變量是普通的變量,定義時在棧中配置設定,引用變量在程式運作到其作用域之外後被釋放。而數組和對象本身在堆中配置設定,即使程式運作到使用new産生數組或者對象的語句所在的代碼塊之外,數組和對象本身占據的記憶體不會被釋放,數組和對象在沒有引用變量指向它的時候,才變為垃圾,不能在被使用,但仍然占據記憶體空間不放,在随後的一個不确定的時間被垃圾回收器收走(釋放掉)。例如:

java中的記憶體管理

 由上圖我們知道,對象名稱p被儲存在了棧記憶體中,具體執行個體儲存在堆記憶體中。也就是說,在棧記憶體中儲存的是堆記憶體空間的通路位址,或者說棧中的變量指向堆記憶體中的變量(Java中的指針)。

堆和棧的比較

 從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的,棧主要是用來執行程式的.而這種不同又主要是由于堆和棧的特點決定的:

 在程式設計中,例如C/C++中,所有的方法調用都是通過棧來進行的,所有的局部變量,形式參數都是從棧中配置設定記憶體空間的。實際上也不是什麼配置設定,隻是從棧頂向上用就行,就好像工廠中的傳送帶一樣,Stack Pointer會自動指引你到放東西的位置,你所要做的隻是把東西放下來就行.退出函數的時候,修改棧指針就可以把棧中的内容銷毀.這樣的模式速度最快, 當然要用來運作程式了.需要注意的是,在配置設定的時候,比如為一個即将要調用的程式子產品配置設定資料區時,應事先知道這個資料區的大小,也就說是雖然配置設定是在程式運作時進行的,但是配置設定的大小多少是确定的,不變的,而這個”大小多少”是在編譯時确定的,不是在運作時.

 堆是應用程式在運作的時候請求作業系統配置設定給自己記憶體,由于從作業系統管理的記憶體配置設定,是以在配置設定和銷毀時都要占用時間,是以用堆的效率非常低.但是堆的優點在于,編譯器不必知道要從堆裡配置設定多少存儲空間,也不必知道存儲的資料要在堆裡停留多長的時間,是以,用堆儲存資料時會得到更大的靈活性。事實上,面向對象的多态性,堆記憶體配置設定是必不可少的,因為多态變量所需的存儲空間隻有在運作時建立了對象之後才能确定.在C++中,要求建立一個對象時,隻需用 new指令編制相關的代碼即可。執行這些代碼時,會在堆裡自動進行資料的儲存.當然,為達到這種靈活性,必然會付出一定的代價:在堆裡配置設定存儲空間時會花掉更長的時間。

方法區

 方法區是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料 (重點)。

 相對而言,垃圾收集行為在這個區域比較少出現,但并非資料進了方法區就永久的存在了,這個區域的記憶體回收目标主要是針對常量池的回收和對類型的解除安裝,

 當方法區無法滿足記憶體配置設定需要時,将抛出OutOfMemoryError異常。

 運作時常量池:

  是方法區的一部分,它用于存放編譯期生成的各種字面量和符号引用。

java中的記憶體管理

總而言之,棧是存儲方法和main需要用到的變量或對象指針

堆是存儲對象的資訊

方法區是存儲類的資訊