這篇文章,我們來(lái)講一下java的內(nèi)存圖及并發(fā)編程的預(yù)備內(nèi)容。
首先,我們來(lái)看一下下面的這兩段代碼:
下面,我們給出上面這兩段代碼在運(yùn)行時(shí)的內(nèi)存結(jié)構(gòu)圖,如下圖所示:
下面,我們來(lái)具體的講解一下。
首先,我們寫了一個(gè)java程序是以.java的文件形式保存在磁盤中的,當(dāng)我們運(yùn)行它的時(shí)候,首先,jdk會(huì)將其編譯為.class文件,用的是javac命令,也是在磁盤中,然后,jre會(huì)去運(yùn)行.class文件,用的是java命令,然后,我們的程序就被運(yùn)行了,這是整個(gè)過(guò)程。
當(dāng)我們需要去運(yùn)行一個(gè)java程序時(shí),或者說(shuō),當(dāng)jdk用javac命令去編譯一個(gè).java文件的時(shí)候,操作系統(tǒng)就會(huì)在內(nèi)存中開(kāi)辟一片區(qū)域,叫java運(yùn)行時(shí)內(nèi)存,里面存儲(chǔ)我們一個(gè)java程序在運(yùn)行時(shí)的所有信息。
java運(yùn)行時(shí)內(nèi)存中會(huì)有方法區(qū),里面存放的是我們類的信息,包括類的成員變量和成員方法。方法區(qū)中還有一塊叫做靜態(tài)方法區(qū),里面存放的是我們類中的靜態(tài)方法。方法區(qū)中的內(nèi)容在類被編譯的時(shí)候就會(huì)生成,生成之后會(huì)將類中的靜態(tài)方法拷貝一份到靜態(tài)方法區(qū)。注意,java中的所有靜態(tài)資源在類被編譯的時(shí)候都會(huì)被初始化。根據(jù)代碼我們可以知道,person類中只有m4是靜態(tài)方法,它會(huì)被拷貝到靜態(tài)方法區(qū)中,Test1中所有的方法都是靜態(tài)方法,都會(huì)被拷貝到靜態(tài)方法區(qū)中。
之后會(huì)有一塊棧區(qū),它是控制方法的執(zhí)行順序及變量的定義域。程序是從main方法開(kāi)始執(zhí)行的,所以首先main方法入棧,然后main方法中創(chuàng)建了3個(gè)person類的實(shí)例,即x1,x2,x3,所以java會(huì)在堆內(nèi)存中創(chuàng)建三個(gè)實(shí)例對(duì)象,這三個(gè)實(shí)例對(duì)象會(huì)存儲(chǔ)類的一切信息,除了靜態(tài)方法。然后main方法調(diào)用m1方法,m1方法入棧,m1方法調(diào)用m2方法,m2方法入棧,m2方法調(diào)用m3方法,m3方法入棧,m3方法中創(chuàng)建person類的實(shí)例x1,所以java在堆內(nèi)存中創(chuàng)建出實(shí)例對(duì)象,然后m3方法執(zhí)行實(shí)例x1的m2方法,所以m3方法中的實(shí)例x1的m2方法入棧,而該實(shí)例的m2方法又調(diào)用該類的m1方法,所以該實(shí)例的m1方法入棧,等m1執(zhí)行結(jié)束后,x1的m1方法出棧,然后x1的m2出棧,然后Test1的m3出棧,然后Test1的m2出棧,然后Test1的m1出棧,然后回到main方法中再依次往下執(zhí)行。這就是一個(gè)java程序執(zhí)行的整個(gè)流程。
以上的內(nèi)存模型只是一種簡(jiǎn)略內(nèi)存模型,更詳細(xì)的內(nèi)存模型大家可以去參考我的JVM系列內(nèi)容。
上面只是單個(gè)線程的,下面來(lái)看一下多線程的。
看一下下面的這段代碼:
看一下輸出結(jié)果:
我們結(jié)合上面單線程的內(nèi)存分布,來(lái)分析一下這段代碼的內(nèi)存分布:
代碼的編譯和方法區(qū)就不說(shuō)了,直接從主方法開(kāi)始說(shuō)。
代碼是從主方法開(kāi)始運(yùn)行的,運(yùn)行主方法的時(shí)候,java會(huì)在棧區(qū)中開(kāi)辟出主線程棧,然后主方法入棧,執(zhí)行,執(zhí)行到第5行的時(shí)候,代碼new了一個(gè)新的線程x1,所以在堆中創(chuàng)建出線程x1的實(shí)例,這個(gè)實(shí)例非常復(fù)雜,但是我們可以將他簡(jiǎn)化為里面有start和run兩個(gè)方法。創(chuàng)建完成之后,同時(shí),在棧區(qū)中創(chuàng)建新的線程x1,在線程x1中,方法run拷貝入棧,準(zhǔn)備執(zhí)行,同時(shí),主線程棧中的代碼也會(huì)執(zhí)行,所以就有我們看到的運(yùn)行結(jié)果:線程x1和主線程交替著打印輸出。
這樣解釋可能不好理解,下面換個(gè)角度解釋一下。
這是我電腦的部分CPU的部分性能信息,我們來(lái)看下面的幾行信息。
進(jìn)程,進(jìn)程的科學(xué)定義是:進(jìn)程是程序在某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),也是操作系統(tǒng)進(jìn)行資源分配和保護(hù)的基本單位。通俗的說(shuō):進(jìn)程就是程序的一次執(zhí)行過(guò)程。進(jìn)程數(shù)213,就是說(shuō),當(dāng)前時(shí)刻,我的電腦上有213個(gè)程序處于“運(yùn)行”狀態(tài)(或者說(shuō)開(kāi)著)。
線程,線程是CPU調(diào)度的最小單位,簡(jiǎn)單來(lái)說(shuō),CPU每次只能運(yùn)行線程,不能運(yùn)行進(jìn)程。線程數(shù)3190,就是說(shuō)當(dāng)前時(shí)刻,我的電腦上一共有3190個(gè)線程,并且這3190個(gè)線程是分布在213個(gè)進(jìn)程中的(進(jìn)程是由線程組成的)
句柄,就是變量,句柄107879,就是說(shuō)當(dāng)前時(shí)刻,我的電腦內(nèi)存中一個(gè)包含107879個(gè)變量。
內(nèi)核,就是CPU的核數(shù),一個(gè)CPU有多少核數(shù),那么這個(gè)CPU在同一時(shí)刻就能執(zhí)行多少個(gè)線程。我的CPU是8核的,說(shuō)明我的電腦在同一時(shí)刻能跑8個(gè)線程。
CPU的核數(shù)是一定的,線程數(shù)是變化的,并且線程數(shù)是遠(yuǎn)遠(yuǎn)大于核數(shù)的,CPU每次只能執(zhí)行8個(gè)線程,那么剩下的線程就只能暫時(shí)處于其他狀態(tài)(這個(gè)操作系統(tǒng)中有介紹),但是一臺(tái)電腦不可能只靠這8個(gè)線程來(lái)運(yùn)行,所以CPU是在不停的做線程切換的,也就是說(shuō)CPU每個(gè)線程執(zhí)行一段時(shí)間然后就切換去執(zhí)行另一個(gè)線程,這就是多線程。
明白了這點(diǎn),我們?cè)倏瓷厦娴拇a,那是兩個(gè)線程,主線程和x1線程,他們兩個(gè)競(jìng)爭(zhēng)者進(jìn)入CPU,然后被CPU執(zhí)行,當(dāng)某個(gè)線程被CPU選中時(shí),那個(gè)這個(gè)線程中的內(nèi)容就會(huì)被執(zhí)行,但是它不是直接執(zhí)行完的,而是會(huì)有線程的切換,當(dāng)它被切換出去了,它就不會(huì)被執(zhí)行了,也就不會(huì)被打印輸出了。這就是上面交替輸出的原因。
前面說(shuō)了線程的切換,線程的狀態(tài)等內(nèi)容,這些是操作系統(tǒng)中的,這里只是簡(jiǎn)單的提到,如果想要具體的了解,可以參考我的操作系統(tǒng)專欄中的內(nèi)容。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-725882.html
這篇文章比較散,就是講了一下一個(gè)java程序在運(yùn)行時(shí)的內(nèi)存結(jié)構(gòu)圖,然后稍微的提到了多線程。這些都是比較基礎(chǔ)的,都是要好好掌握。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-725882.html
到了這里,關(guān)于并發(fā)編程——1.java內(nèi)存圖及相關(guān)內(nèi)容的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!