HotSpot 自动内存管理笔记与实战
1.對象的創建
虛擬機遇到一條new指令時,首先會去檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,并且檢查這個符號引用代表的類是否已被加載、解析和初始化過。如果沒有,則必須先進行相應的類的加載。
2、對象的訪問定位
簡歷對象是為了使用對象,我們的java程序需要通過棧上的refrerence數據來操作堆上的具體對象。由于reference類型在java虛擬機規范中只規定了一個指向對象的引用,并沒有定義這個引用應該通過何種方式去定位、訪問堆中的對象的具體位置,所以對象訪問方式也是取決于虛擬機實現而定的。目前主流的訪問方式有使用句柄和直接指針兩種。
使用句柄:那么 java堆中會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。
?
使用直接指針:那么Java堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,而reference中存儲的直接就是對象地址。
使用句柄來訪問的最大好處就是refernce中存儲的是穩定的句柄地址,在對象被移動時,將會改變句柄中的實例數據指針,而reference本身不需要修改。
使用直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷,由于對象的訪問在JAVA中非常頻繁,因此這類開銷積少成多后也是一項非常可觀的執行成本。
?
3、JAVA堆溢出
JAVA堆用于存儲對象實例,不斷地創建對象,并保證GC, Root到對象之間有可達路徑來避免垃圾回收機制清除這些對象,那么在對象數量到達最大堆的容量限制后就會產生內存溢出異常。
下面設置運行參數為 -Xms521M -Xmx512M -XX:+HeapDumpOnOutOfMemoryError,讓虛擬機出現內存溢出異常時Dump出當前的內存堆轉儲快照以便事后進行分析。
package JVMtest;import java.util.ArrayList; import java.util.List;public class HeapOOM {static class OOMObject{}public static void main(String[] args){List<OOMObject> list = new ArrayList<OOMObject>();while(true){list.add(new OOMObject());}} }將堆的最小值-Xms參數與最大值-Xmx參數設置為一樣即可避免堆自動擴展
?
4、虛擬機棧和本地方法棧溢出
棧容量只由-Xss參數設定。關于虛擬機棧和本地方法棧,在JAVA虛擬機規范中描述了兩種異常:
(1)如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常。
(2)如果虛擬機在擴展棧時無法申請到足夠的內存空間,則拋出OutOfMemoryError異常。
測試時,通過不斷地創建線程的方式倒是可以產生內存溢出異常,但這樣產生的內存溢出異常與棧空間是否足夠大并不存在任何聯系,或者再準確的說,在這種情況下,為每個線程的棧分配的內存越大,反而越容易產生內存溢出的異常。原因是,操作系統分配給每個進程的內存是有限制的,譬如32為的Windows限制為2GB,虛擬機提供了參數來控制JAVA堆和方法去的兩部分最大的內存值。那么剩下的內存為,操作系統限制的2GB-Xms(最大堆容量)-MaxPermSize(最大方法區容量),程序計數器消耗內存很小,可以忽略。如果虛擬機本身消耗的內存不計算在內,剩下的內存就由虛擬機和本地方法棧瓜分了。每個線程分配到的棧容量越大,可以建立的線程數量自然就減少,建立線程時,就很容易把剩下的內存耗盡。
package JVMtest;public class JavaVMStackOOM {private void dontStop(){while(true){}}public void stackLeankByThread(){while(true){Thread thread = new Thread(new Runnable(){@Overridepublic void run() {dontStop();}});thread.start();}}public static void main(String args[]){JavaVMStackOOM oom = new JavaVMStackOOM();oom.stackLeankByThread();} }?
5、方法區和運行時異常量池溢出
比如String.intern()是一個Native方法,作用是,如果字符串常量池中已經包含一個等于此String對象的字符串,則返回帶包翅中這個字符串的String對象;否則,將此String對象包含的字符串添加到常量池中,并且返回此String對象的引用。
方法區用于存放Class的相關信息,如類名、訪問修飾符、常量池、字段描述、方法描述等。這些區域的測試,基恩的思路是運行時產生大量的類去填滿方法區,直到溢出。
package JVMtest;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;public class JavaMethodAreaOOM {public static void main(String args[]){while(true){Enhancer enhancer = new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor(){@Overridepublic Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {return proxy.invokeSuper(obj,args);}});enhancer.create();}}static class OOMObject{} }
?
總結
以上是生活随笔為你收集整理的HotSpot 自动内存管理笔记与实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ZigBee] 9、ZigBee之AD
- 下一篇: Activity生命周期的补充