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