JVM內存分配和垃圾收集策略
java內存區域
程序計數器
因為java可以多線程並發執行,因此,為了線程切換後能恢復到正確的執行位置,每個線程都需要一個獨立的程序計數器。記錄正在執行的虛擬機字節碼指令的地址。
這個區域不會產生內存溢出異常。
棧
java虛擬機棧
棧中主要存放了編譯期可知的四類八種基本數據類型存(邏輯型 boolean、文本型char、整數型byte、short、int、float、浮點數型double、long),對象引用類型,和對象引用類型(reference)。
本地方法棧
本地方法棧和java虛擬機棧所發揮的作用非常相似,他們之前的區別是虛擬機棧為虛擬機執行java方法服務。而本地方法棧是為虛擬機使用到的Native方法服務。
-Xss參數可以設置本地方法棧的內存上限。
如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常。
程序中 遞歸如果找不到出口也會拋出此異常(不斷的進行入棧操作)
堆
用於存放對象的實列
可通過-Xms和Xmx來擴展堆的大小。因為現在的收集器基本都采用分代收集算法,所以java堆中還可以細分:新生代和老年代
如果在堆中沒有內存完成實列分配,並且堆也無法再擴展時,將會拋出OutOfMemoryError異常。-Xmx可以設置堆內存的上限 -Xms 可以設置內存初始化的大小
方法區(永久代、也叫非堆)
用於儲存已被虛擬機加載的類的信息(編譯後的class)、常量、靜態變量、即時編譯器編譯後的代碼等數據,Class在被Loader時就會被放到方法區(PermGen space)中
垃圾收集行為在這個區比較少出現的,有點類似它的名字,永久代
當方法區無法滿足內存分配需求時,將拋出OutOfMemoryErro(後面會跟PermGen space字符串)異常。-XX:MaxPermSize可以設置方法區的上限。
運行時常量池(方法區的一部分)
用於存放編譯期生成的各種字面量和符號的引用,既然運行時常量池是方法區的一部分,自然收到方法區內存的限制,當常量池無法再申請到內存時會拋出OutOfMemoryError異常。
JAVA的String聲明的變量就是放在運行時常量池中比如String str="test"; 。但是 String str=new String("test");是new一個對象 是放在堆中
每當我們創建字符串常量時,JVM會首先檢查字符串常量池,如果該字符串已經存在常量池中,那麽就直接返回常量池中的實例引用。
如果字符串不存在常量池中,就會實例化該字符串並且將其放到常量池中。由於String字符串的不可變性我們可以十分肯定常量池中一定不存在兩個相同的字符串
以上
如果線程請求的棧深度大於虛擬機所允許的最大深度,將拋出StackOverflowError異常。
如果虛擬機在擴展時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。可通過-Xmx和-XX:MaxPermSize可以設置內存和方法區上限。-Xmx2048m -XX:MaxPermSize=512m
把-Xmx 和-Xms大小設置一樣內存大小,可以提高JVM的運行性能(取消掉伸縮區)
JVM垃圾收集算法
標記——清除算法
首先標記出所有需要回收的對象,在標記完成後統一回收。
缺點:這種回收算法會產生大量不連續的內存碎片。以後程序運行過程中產生較大的對象時,無法找到足夠連續的內存 而不得不提前觸發另一次垃圾收集動作。
復制算法
將內存按容量劃分大小相等的兩塊,每次只使用一塊。當這一塊的內存用完了,就將還存活的對象復制到另外一塊內存上面。
缺點:將內存的使用率縮小了一半
標記——整理算法
首先標記出所有需要回收的對象,然後讓所有存貨的對象都向一端移動。然後直接清理掉端邊界意外的內存
分代收集算法
我們jdk采用的應該就是分代收集算法。根據對象存貨周期的不同將內存劃分為幾塊,針對每一塊使用不同的算法
新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用復制算法,只需要對少量存活的對象復制就可以進行收集。
老年代中,因為對象存貨率高,沒有額外空間對它進行分配擔保,就需要使用 標記——整理或者標記——清楚算法。
基於JDK命令行工具的監控
更新中。。。。。。
JVM內存分配和垃圾收集策略