java程序员遇到的问题_Java 程序员平时最常遇到的故障:系统OOM (一)
作為 Java 程序員而言,先不考慮自己系統(tǒng)外部依賴的緩存、消息隊列、數(shù)據(jù)庫等等東西掛掉,就我們自己系統(tǒng)本身而言,最常見的掛掉的原因是什么?
其實就是系統(tǒng)OOM,也就是所謂的內(nèi)存溢出!
什么是內(nèi)存溢出?在哪些區(qū)域會發(fā)生內(nèi)存溢出?
運行一個 Java 系統(tǒng)就是運行一個JVM進(jìn)程
首先的話呢,大家得先搞明白一個事情,就是我們平時說啟動一個Java系統(tǒng),其實本質(zhì)就是啟動一個JVM進(jìn)程。
咱們就用最最基本的情況來給大家演示一下好了,比如說下面的一段代碼,是每個Java初學(xué)者都會寫的一段代碼:
那么大家知道,當(dāng)你在Eclipse或者Intellij IDEA中寫好這個代碼,然后通過IDE來運行這個代碼的時候,會發(fā)生哪些事情嗎?
首先,我們專欄最早的幾篇文章就給大家說過,我們寫好的代碼他都是后綴為“.java”的源代碼,這個代碼是不能運行的。
所以第一步就是這份“.java”源代碼文件必須先編譯成一個“.class”字節(jié)碼文件,這個字節(jié)碼文件才是可以運行的,如下圖所示。
接著對于這種編譯好的字節(jié)碼文件,比如HelloWorld.class,如果里面包含了main方法,接下來我們就可以用“java命令”來在命令行執(zhí)行這個字節(jié)碼文件了
實際上一旦你執(zhí)行“java命令”,相當(dāng)于就會啟動一個JVM進(jìn)程。這個JVM進(jìn)程就會負(fù)責(zé)去執(zhí)行你寫好的那些代碼,如下圖所示。
所以首先要清楚第一點,運行一個Java系統(tǒng),本質(zhì)上就是啟動一個JVM進(jìn)程,這個JVM進(jìn)程負(fù)責(zé)來執(zhí)行你寫好的一大堆代碼。只要你的Java系統(tǒng)中包含一個main方法,接著JVM進(jìn)程就會從你指定的這個main方法入手,開始執(zhí)行你寫的代碼。
到底執(zhí)行哪些代碼:JVM得加載你寫的類
Java是一個面向?qū)ο蟮恼Z言,所以最最基本的代碼組成單元就是一個一個的類,平時我們說寫Java代碼,不就是寫一個一個的類嗎?是不是。
然后在一個一個的類里我們會定義各種變量,方法,數(shù)據(jù)結(jié)構(gòu),通過if else之類的語法,寫出來各種各樣的系統(tǒng)業(yè)務(wù)邏輯,這就是所謂的編程了。
所以JVM既然要執(zhí)行你寫的代碼,首先當(dāng)然得把你寫好的類加載到內(nèi)存里來啊!
所以JVM的內(nèi)存區(qū)域里大家都知道,有一塊區(qū)域叫做永久代,當(dāng)然JDK 1.8以后都叫做Metaspace了,我們也用最新的說法好了。
這塊內(nèi)存區(qū)域就是用來存放你系統(tǒng)里的各種類的信息的,包括JDK自身內(nèi)置的一些類的信息,都在這塊區(qū)域里。
JVM有類加載器和一套類加載的機(jī)制,我們在專欄最開始的時候都說過了,這里不再贅述,他會負(fù)責(zé)把我們寫好的類從編譯好的“.class”字節(jié)碼文件里加載到內(nèi)存里來,如下圖。
好,那么既然有這么一塊Metaspace區(qū)域是用來存放類信息的,那是不是有可能在這個Metaspace區(qū)域里就會發(fā)生OOM?
沒錯,是有這種可能的。
Java虛擬機(jī)棧:讓線程執(zhí)行各種方法
大家都知道,我們寫好的那些Java代碼雖然是一個一個的類,但是其實核心的代碼邏輯一般都是封裝在類里面的各種方法中的
比如JVM已經(jīng)加載了我們寫好的HelloWorld類到內(nèi)存里了,接著怎么執(zhí)行他里面的代碼呢?
Java語言中的一個通用的規(guī)則,就是一個JVM進(jìn)程總是從main方法開始執(zhí)行的,所以我們既然在HelloWorld中寫了一個main()方法,那么當(dāng)然得執(zhí)行這個方法中的代碼了。
但是等一等,JVM進(jìn)程里的誰去執(zhí)行main()方法的代碼?
其實我們所有的方法執(zhí)行,都必須依賴JVM進(jìn)程中的某個線程去執(zhí)行,你可以理解為線程才是執(zhí)行我們寫的代碼的核心主體。
JVM進(jìn)程啟動之后默認(rèn)就會有一個main線程,這個main線程就是專門負(fù)責(zé)執(zhí)行main()方法的。
大家如下圖所示。
現(xiàn)在又有一個問題了,在main()方法里定義了一個局部變量,“message”,那么大家回憶一下,這些方法里的局部變量可能會有很多,那么這些局部變量是放在哪里的呢?
很簡單,每個線程都有一個自己的虛擬機(jī)棧,就是所謂的棧內(nèi)存。
然后這個線程只要執(zhí)行一個方法,就會為方法創(chuàng)建一個棧楨,將棧楨放入自己的虛擬機(jī)棧里去,然后在這個棧楨里放入方法中定義的各種局部變量,如下圖所示
好,現(xiàn)在問題來了,大家如果還記得之前我們講過的一個參數(shù),應(yīng)該都知道,我們是可以設(shè)置JVM中每個線程的虛擬機(jī)棧的內(nèi)存大小的,一般是設(shè)置為1MB。
那么既然每個線程的虛擬機(jī)棧的內(nèi)存大小是固定的,是否可能會發(fā)生虛擬機(jī)棧的內(nèi)存溢出?
沒錯,所以第二塊可能發(fā)生OOM的區(qū)域,就是每個線程的虛擬機(jī)棧內(nèi)存。
堆內(nèi)存:放我們創(chuàng)建的各種對象
最后我們知道,我們寫好的代碼里,特別在一些方法中,可能會頻繁的創(chuàng)建各種各樣的對象,這些對象都是放在堆內(nèi)存里的,如下圖所示。
而且我們通過之前的學(xué)習(xí),也都知道了一點,通常我們在JVM中分配給堆內(nèi)存的空間其實一般是固定的
既然如此,我們還不停在堆內(nèi)存里創(chuàng)建對象,是不是說明,堆內(nèi)存也有可能會發(fā)生內(nèi)存溢出?
沒錯,第三塊可能發(fā)生內(nèi)存溢出的區(qū)域,就是堆內(nèi)存空間!
總結(jié)
以上是生活随笔為你收集整理的java程序员遇到的问题_Java 程序员平时最常遇到的故障:系统OOM (一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java用if语句调用方法_J2SE中m
- 下一篇: java注解的执行顺序_深入理解Spri