Java 内存模型(一)
打算花比較長的篇幅來描述下自己理解的JVM,盡量描述的清晰易懂一些,從簡單慢慢到慢慢深入,一方面自己也復(fù)習(xí)一下,一方面也供大家參考,少走些彎路。鑒于本人水平有限,如有錯誤的地方,歡迎指出,感謝。
?
一段廢話引出,大家都知道java有JVM,那JVM有個非常方便的自動內(nèi)存管理機(jī)制,致使java開發(fā)人員不再需要為每個new操作去寫配對的delete/free代碼,而且不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出的問題(不容易并非不可能),不過真因?yàn)榇艘坏┏霈F(xiàn)了內(nèi)存泄漏或者溢出的問題,如果不了解JVM怎么樣使用內(nèi)存的,那么排查起來也會比較困難。
?
我們初始學(xué)習(xí)java時候很多人關(guān)注就是java的堆棧,這種分法比較粗糙,但是易與理解,實(shí)際上遠(yuǎn)遠(yuǎn)不止于此,但是今天我們就先按粗淺的方式去解釋下,后面我們在逐步深入。按我的習(xí)慣,我們先看個例子。
1 public class BaseTest { 2 public static void main(String[] args) throws Exception{ 3 ArrayList list=new ArrayList(); 4 while(true){ 5 list.add(new FinalBean()); 6 } 7 } 8 }上面這段代碼最終的結(jié)果:
1 java.lang.OutOfMemoryError: GC overhead limit exceeded 2 Dumping heap to java_pid1288.hprof ... 3 Heap dump file created [34835053 bytes in 0.247 secs] 4 Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded 5 at java.util.Arrays.copyOf(Arrays.java:3332) 6 at java.lang.String.concat(String.java:2032) 7 at com.cz.smart.center.FinalBean.<init>(FinalBean.java:13) 8 at com.cz.smart.center.BaseTest.main(BaseTest.java:14)為什么會出現(xiàn)這種結(jié)果呢??
我們通過創(chuàng)建實(shí)例對象?new FinalBean(),會開辟堆內(nèi)存空間,往list列表里面加,導(dǎo)致實(shí)例對象沒法回收,一直到無法申請到新的內(nèi)存,導(dǎo)致爆內(nèi)存溢出,那有沒有考慮過,為什么實(shí)例對象的內(nèi)存無法進(jìn)行回收呢?
?
再看一個例子
public class BaseTest {int i = 0;public static void main(String[] args) throws Exception{BaseTest b = new BaseTest();b.addStackLength();}public void addStackLength(){i++;System.out.println(i);addStackLength();} }
上面這段代碼最終的結(jié)果:
938 939 Exception in thread "main" java.lang.StackOverflowErrorat sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)at sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:691)at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:579)at sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:271)at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:125)at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)at java.io.PrintStream.write(PrintStream.java:526)at java.io.PrintStream.print(PrintStream.java:597)at java.io.PrintStream.println(PrintStream.java:736)
為什么這段代碼會報(bào)這個錯呢?StackOverflowError 棧溢出錯誤。
?遞歸調(diào)用,死循環(huán),超過棧的深度,導(dǎo)致拋出異常,那這段代碼再內(nèi)存從怎么跑的呢?我畫圖解釋下:
我們知道棧是線程獨(dú)享的,每個線程都會有自己獨(dú)立的棧,而每調(diào)用一個方法會產(chǎn)生一個棧針進(jìn)行入棧。
1.在代碼b.addStackLength 時候,會入棧addStackLength方法的棧針,棧針包括局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息(這些后面會詳細(xì)講解)。
2.進(jìn)入方法addStackLength的時候,先執(zhí)行i++,然后輸出i++信息,發(fā)現(xiàn)該方法又調(diào)用了addStackLength的方法。
3.于是又會入棧一個addStackLength方法的棧針,以此循環(huán),由于是死循環(huán),入棧的棧針超過了棧的深度,拋出StackOverflowError 錯誤。
?
注:如果是遞歸調(diào)用而沒有死循環(huán),那情況就是在最底層的方法執(zhí)行完之后,最底層方法就會出棧,調(diào)轉(zhuǎn)到上一個調(diào)用該方法的下一行,以此類推持續(xù)出棧,這就是棧的先入后出。
?
?后續(xù)一步步拆分堆和棧內(nèi)容信息,未完待續(xù)。。。
轉(zhuǎn)載于:https://www.cnblogs.com/HA-Tinker/p/10687097.html
總結(jié)
以上是生活随笔為你收集整理的Java 内存模型(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 未能加载文件或程序集 ICSharpCo
- 下一篇: Python网络编程—TCP套接字之HT