程序計數器是一塊較小的內存空間,主要功能是作為當前線程所執行字節碼當前行號指示器。當其運行java方法時,指向的是虛擬機字節碼指令的地址,當其運行native方法時,則為空。程序計數器是java虛擬機規范中唯一沒有OutOfMemoryError情況的區域.
虛擬機棧描述的主要是java方法執行的內存模型,用于存儲方法調用時的創建的棧幀,棧幀存儲了局部變量表,操作數棧,動態連接,方法出口等信息。線程請求的棧深度大于虛擬機所允許的深度時,拋出stackoverflowError異常,如果虛擬機可以動態擴展,但擴展時無法申請足夠的內存,則會拋出OutOfMemoryError異常。
本地方法棧實現的功能和虛擬機棧類似,不過它處理的是native方法。
堆的主要功能是存放對象實例,幾乎所有的數組和對象實例都是在堆中分配.并且它也是垃圾回收的主要區域.如果堆中沒有內存來分配實例,并且不能擴展,則會拋出outOfMemoryError
方法區用于存放已被虛擬機加載的類信息,常量,靜態變量,即時編譯編譯后的代碼(動態代理等)等數據。當方法區無法滿足分配需求時,將拋出OutOfMemoyError異常
jdk1.4中新加入了NIO類,引入了一種基于通道與緩沖區的I/O方式,它可以使用Native函數直接分配堆外內存,然后通過一個存儲在java堆中的DirectByteBuffer對象作為這塊內存的引用進行操作。這樣避免了java堆和native堆之間來回復制數據,提高了性能。
虛擬機遇到一條new指令時,首先將先檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,并且檢查這個符號是否被加載,解析,初始化過。如果沒有,則執行類加載過程。在類加載后,就是對對象分配內存,對于java內存規整的情況,使用的是"指針碰撞",而不規整的則采用"空閑列表"的分配方式。經過上面的操作后,執行<init>將對象按照程序員的意愿進行初始化。
在hotspot虛擬機中,對象的內存中的存儲的布局可以分為三個部分對象頭 ,實例數據 ,對齊填充。對象頭主要有兩部分信息,第一部分為用于存儲對象自身的運行時數據,如哈希碼,GC分代年齡,鎖狀態標志,線程持有的鎖,偏向線程ID,偏向時間戳等。另一部分為類型指針,指向類的元數據信息。對于數組還有一部分為數組長度信息。實例數據存儲的即為各種類型的字段內容。
主要是使用句柄和直接指針兩種方式。句柄是java堆中會分出一塊內存來作為句柄池,reference存儲的是對象的句柄地址,而句柄中存儲的是對象數據和類數據地址。直接地址方式是reference直接指向的就對象地址句柄的優勢是對象移動不用修改reference指針,只需要修改句柄指針,而直接指針的優勢是訪問速度更快.
import java.util.ArrayList;import java.util.List;/** * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * -Xms設置堆的最小值為20MB * -Xmx設置堆的最大值為20MB * -XX:+HeapDumpOnOutOfMemoryError內存溢出時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設置堆棧內存 * */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); } }}結果: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 方法區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設置方法區的大小 * 這兩個參數在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{ }}
新聞熱點
疑難解答