牛客網刷題筆記---堆和棧

JVM

 

關於堆和棧

 

Java 把內存劃分成兩種:一種是棧內存,另一種是堆內存。

 

棧式存儲:

 

在函數中定義的一些基本類型的變量和對象的引用變量都是在函數的棧內存中分配,當超過變量的作用域後,Java 會自動釋放掉爲該變量分配的內存空間,該內存空間可以立即被另作它用。

 

優點:存取速度比堆要快,僅次於寄存器,棧數據可以共享。

 

缺點:存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量(,int, short, long, byte, float, double, boolean, char)和對象句柄。

 

堆式存儲:

 

堆內存用來存放由 new 創建的對象和數組,在堆中分配的內存,由 Java 虛擬機的自動垃圾回收器來管理。

 

優點:堆是由垃圾回收來負責的,堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,因爲它是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些不再使用的數據。

 

缺點是:由於要在運行時動態分配內存,存取速度較慢。

 

jvm佈局

 

棧有兩部分,Java線程棧以及本地方法棧。裏面存放的都是棧幀,一個棧幀代表的就是一個函數的調用,在棧幀裏面存放了函數的形參,函數的局部變量,返回地址等,在這個基本架構圖中,可以看出JVM還定義了一個本地方法棧,本地方法棧是爲Java調用本地方法【這些本地方法是由其他語言編寫的】服務的。

 

JVM中棧有兩個,但是堆只有一個,每一個線程都有自已的線程棧【線程棧的大小可以通過設置JVM的-xss參數進行配置,32位系統下,JDK5.0以後每個線程堆棧大小爲1M,以前每個線程堆棧大小爲256K】,線程棧裏面的數據屬於該線程私有,但是所有的線程都共享一個堆空間,堆中存放的是對象數據,什麼是對象數據,排除法,排除基本類型以及引用類型以外的數據都將放在堆空間中。其中方法區和堆是所有線程共享的數據區。

 

本地方法棧

 

在HotSpot虛擬機將本地方法棧和虛擬機棧合二爲一,它們的區別在於,虛擬機棧爲執行Java方法服務,而本地方法棧則爲虛擬機使用到的Native方法服務。

 

pc寄存器

 

程序計數器,在CPU的寄存器中有一個PC寄存器,存放下一條指令地址,這裏,虛擬機不使用CPU的程序計數器,自己在內存中設立一片區域來模擬CPU的程序計數器。只有一個程序計數器是不夠的,當多個線程切換執行時,那就單個程序計數器就沒辦法了,虛擬機規範中指出,每一條線程都有一個獨立的程序計數器。注意,Java虛擬機中的程序計數器指向正在執行的字節碼地址,而不是下一條。

 

java棧

 

Java虛擬機棧也是線程私有的,虛擬機棧描述的是Java方法執行的內存模型:每個方法執行的時候都會創建一個棧幀,用於存放局部變量表,操作數棧,動態鏈接,方法出口等信息。每一個方法從調用直到執行完成的過程都對應着一個棧幀在虛擬機中的入棧到出棧的過程。我們平時把內存分爲堆內存和棧內存,其中的棧內存就指的是虛擬機棧的局部變量表部分。局部變量表存放了編譯期可以知道的基本數據類型(boolean、byte、char、short、int、float、long、double),對象引用(可能是一個指向對象起始地址的引用指針,也可能指向一個代表對象的句柄或者其他與此對象相關的位置),和返回後所指向的字節碼的地址。其中64 位長度的long 和double 類型的數據會佔用2個局部變量空間(Slot),其餘的數據類型只佔用1個。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。當遞歸層次太深時,會引發java.lang.StackOverflowError,這是虛擬機棧拋出的異常。

 

 

Java 堆是被所有線程共享的一塊內存區域,在虛擬機啓動時創建。這個區域是用來存放對象實例的,幾乎所有對象實例都會在這裏分配內存。堆是Java垃圾收集器管理的主要區域(GC堆),垃圾收集器實現了對象的自動銷燬。Java堆可以細分爲:新生代和老年代;再細緻一點的有Eden空間,From Survivor空間,ToSurvivor空間等。Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可,就像我們的磁盤空間一樣。可以通過-Xmx和-Xms控制

 

方法區

 

方法區也叫永久代。在過去(自定義類加載器還不是很常見的時候),類大多是”static”的,很少被卸載或收集,因此被稱爲“永久的(Permanent)”。永久代也是各個線程共享的區域,它用於存儲已經被虛擬機加載過的類信息,常量,靜態變量(JDK7中被移到Java堆),即時編譯期編譯後的代碼(類方法)等數據。從JDK7開始永久代的移除工作,貯存在永久代的一部分數據已經轉移到了Java Heap或者是Native Heap。但永久代仍然存在於JDK7,並沒有完全的移除:符號引用(Symbols)轉移到了native heap;字面量(interned strings)轉移到了java heap;類的靜態變量(class statics)轉移到了java heap。隨着JDK8的到來,JVM不再有PermGen。但類的元數據信息(metadata)還在,只不過不再是存儲在連續的堆空間上,而是移動到叫做“Metaspace”的本地內存(Native memory)中。

在JVM***享數據空間劃分如下圖所示

 

VM***享數據空間可以分成三個大區,新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation),其中JVM堆分爲新生代和老年代

 

新生代可以劃分爲三個區,Eden區(存放新生對象),兩個倖存區(From Survivor和To Survivor)(存放每次垃圾回收後存活的對象)

 

永久代管理class文件、靜態對象、屬性等

 

JVM垃圾回收機制採用“分代收集”:新生代採用複製算法,老年代採用標記清理算法。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章