Java 内存模型(一)
打算花比較長(zhǎng)的篇幅來(lái)描述下自己理解的JVM,盡量描述的清晰易懂一些,從簡(jiǎn)單慢慢到慢慢深入,一方面自己也復(fù)習(xí)一下,一方面也供大家參考,少走些彎路。鑒于本人水平有限,如有錯(cuò)誤的地方,歡迎指出,感謝。
?
一段廢話引出,大家都知道java有JVM,那JVM有個(gè)非常方便的自動(dòng)內(nèi)存管理機(jī)制,致使java開發(fā)人員不再需要為每個(gè)new操作去寫配對(duì)的delete/free代碼,而且不容易出現(xiàn)內(nèi)存泄漏和內(nèi)存溢出的問(wèn)題(不容易并非不可能),不過(guò)真因?yàn)榇艘坏┏霈F(xiàn)了內(nèi)存泄漏或者溢出的問(wèn)題,如果不了解JVM怎么樣使用內(nèi)存的,那么排查起來(lái)也會(huì)比較困難。
?
我們初始學(xué)習(xí)java時(shí)候很多人關(guān)注就是java的堆棧,這種分法比較粗糙,但是易與理解,實(shí)際上遠(yuǎn)遠(yuǎn)不止于此,但是今天我們就先按粗淺的方式去解釋下,后面我們?cè)谥鸩缴钊搿0次业牧?xí)慣,我們先看個(gè)例子。
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)為什么會(huì)出現(xiàn)這種結(jié)果呢??
我們通過(guò)創(chuàng)建實(shí)例對(duì)象?new FinalBean(),會(huì)開辟堆內(nèi)存空間,往list列表里面加,導(dǎo)致實(shí)例對(duì)象沒法回收,一直到無(wú)法申請(qǐng)到新的內(nèi)存,導(dǎo)致爆內(nèi)存溢出,那有沒有考慮過(guò),為什么實(shí)例對(duì)象的內(nèi)存無(wú)法進(jìn)行回收呢?
?
再看一個(gè)例子
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)
為什么這段代碼會(huì)報(bào)這個(gè)錯(cuò)呢?StackOverflowError 棧溢出錯(cuò)誤。
?遞歸調(diào)用,死循環(huán),超過(guò)棧的深度,導(dǎo)致拋出異常,那這段代碼再內(nèi)存從怎么跑的呢?我畫圖解釋下:
我們知道棧是線程獨(dú)享的,每個(gè)線程都會(huì)有自己獨(dú)立的棧,而每調(diào)用一個(gè)方法會(huì)產(chǎn)生一個(gè)棧針進(jìn)行入棧。
1.在代碼b.addStackLength 時(shí)候,會(huì)入棧addStackLength方法的棧針,棧針包括局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接,方法出口等信息(這些后面會(huì)詳細(xì)講解)。
2.進(jìn)入方法addStackLength的時(shí)候,先執(zhí)行i++,然后輸出i++信息,發(fā)現(xiàn)該方法又調(diào)用了addStackLength的方法。
3.于是又會(huì)入棧一個(gè)addStackLength方法的棧針,以此循環(huán),由于是死循環(huán),入棧的棧針超過(guò)了棧的深度,拋出StackOverflowError 錯(cuò)誤。
?
注:如果是遞歸調(diào)用而沒有死循環(huán),那情況就是在最底層的方法執(zhí)行完之后,最底層方法就會(huì)出棧,調(diào)轉(zhuǎn)到上一個(gè)調(diào)用該方法的下一行,以此類推持續(xù)出棧,這就是棧的先入后出。
?
?后續(xù)一步步拆分堆和棧內(nèi)容信息,未完待續(xù)。。。
轉(zhuǎn)載于:https://www.cnblogs.com/HA-Tinker/p/10687097.html
總結(jié)
以上是生活随笔為你收集整理的Java 内存模型(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 未能加载文件或程序集 ICSharpCo
- 下一篇: Java中字符串的常用属性与方法