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