程序計數(shù)器是一塊較小的內(nèi)存空間,主要功能是作為當(dāng)前線程所執(zhí)行字節(jié)碼當(dāng)前行號指示器。當(dāng)其運行java方法時,指向的是虛擬機字節(jié)碼指令的地址,當(dāng)其運行native方法時,則為空。程序計數(shù)器是java虛擬機規(guī)范中唯一沒有OutOfMemoryError情況的區(qū)域.
虛擬機棧描述的主要是java方法執(zhí)行的內(nèi)存模型,用于存儲方法調(diào)用時的創(chuàng)建的棧幀,棧幀存儲了局部變量表,操作數(shù)棧,動態(tài)連接,方法出口等信息。線程請求的棧深度大于虛擬機所允許的深度時,拋出stackoverflowError異常,如果虛擬機可以動態(tài)擴展,但擴展時無法申請足夠的內(nèi)存,則會拋出OutOfMemoryError異常。
本地方法棧實現(xiàn)的功能和虛擬機棧類似,不過它處理的是native方法。
堆的主要功能是存放對象實例,幾乎所有的數(shù)組和對象實例都是在堆中分配.并且它也是垃圾回收的主要區(qū)域.如果堆中沒有內(nèi)存來分配實例,并且不能擴展,則會拋出outOfMemoryError
方法區(qū)用于存放已被虛擬機加載的類信息,常量,靜態(tài)變量,即時編譯編譯后的代碼(動態(tài)代理等)等數(shù)據(jù)。當(dāng)方法區(qū)無法滿足分配需求時,將拋出OutOfMemoyError異常
jdk1.4中新加入了NIO類,引入了一種基于通道與緩沖區(qū)的I/O方式,它可以使用Native函數(shù)直接分配堆外內(nèi)存,然后通過一個存儲在java堆中的DirectByteBuffer對象作為這塊內(nèi)存的引用進行操作。這樣避免了java堆和native堆之間來回復(fù)制數(shù)據(jù),提高了性能。
虛擬機遇到一條new指令時,首先將先檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用,并且檢查這個符號是否被加載,解析,初始化過。如果沒有,則執(zhí)行類加載過程。在類加載后,就是對對象分配內(nèi)存,對于java內(nèi)存規(guī)整的情況,使用的是"指針碰撞",而不規(guī)整的則采用"空閑列表"的分配方式。經(jīng)過上面的操作后,執(zhí)行<init>將對象按照程序員的意愿進行初始化。
在hotspot虛擬機中,對象的內(nèi)存中的存儲的布局可以分為三個部分對象頭 ,實例數(shù)據(jù) ,對齊填充。對象頭主要有兩部分信息,第一部分為用于存儲對象自身的運行時數(shù)據(jù),如哈希碼,GC分代年齡,鎖狀態(tài)標(biāo)志,線程持有的鎖,偏向線程ID,偏向時間戳等。另一部分為類型指針,指向類的元數(shù)據(jù)信息。對于數(shù)組還有一部分為數(shù)組長度信息。實例數(shù)據(jù)存儲的即為各種類型的字段內(nèi)容。
主要是使用句柄和直接指針兩種方式。句柄是java堆中會分出一塊內(nèi)存來作為句柄池,reference存儲的是對象的句柄地址,而句柄中存儲的是對象數(shù)據(jù)和類數(shù)據(jù)地址。直接地址方式是reference直接指向的就對象地址句柄的優(yōu)勢是對象移動不用修改reference指針,只需要修改句柄指針,而直接指針的優(yōu)勢是訪問速度更快.
import java.util.ArrayList;import java.util.List;/** * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * -Xms設(shè)置堆的最小值為20MB * -Xmx設(shè)置堆的最大值為20MB * -XX:+HeapDumpOnOutOfMemoryError內(nèi)存溢出時dump堆信息 */public class HeapOOM { static class OOMObject{ } public static void main(String[] args) { List<OOMObject> list = new ArrayList<>() ; while(true){ list.add(new OOMObject()) ; } } }輸出:java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid6992.hPRof ...Heap dump file created [27980588 bytes in 0.197 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Unknown Source) at java.util.Arrays.copyOf(Unknown Source) at java.util.ArrayList.grow(Unknown Source) at java.util.ArrayList.ensureExplicitCapacity(Unknown Source) at java.util.ArrayList.ensureCapacityInternal(Unknown Source) at java.util.ArrayList.add(Unknown Source) at com.HeapOOM.main(HeapOOM.java:20)3.2 虛擬機棧和本地方法棧溢出
/** * -Xss128k * -Xss設(shè)置堆棧內(nèi)存 * */public class JVMSOF { private int stackDepth = 1; public void stackLeak(){ stackDepth++ ; stackLeak(); } public static void main(String[] args) { JVMSOF jvmsof = new JVMSOF() ; try{ jvmsof.stackLeak(); }catch(Throwable e){ e.printStackTrace(); System.out.println(jvmsof.stackDepth); } }}結(jié)果:986Exception in thread "main" java.lang.StackOverflowError at com.JVMSOF.stackLeak(JVMSOF.java:13) at com.JVMSOF.stackLeak(JVMSOF.java:14) at com.JVMSOF.stackLeak(JVMSOF.java:14)3.3 方法區(qū)OOM
import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;/** * -XX:PermSize=10M -XX:MaxPermSize=10M * -XX:PermSize設(shè)置方法區(qū)的大小 * 這兩個參數(shù)在java8中忽略,并且下面代碼在java8后沒有memoryOutOfError異常 * */public class RuntimeConstantPoolOOM { public static void main(String[] args) { while(true){ Enhancer enhancer = new Enhancer() ; enhancer.setSuperclass(RCPOOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhancer.create() ; } } static class RCPOOMObject{ }}
|
新聞熱點
疑難解答
圖片精選