一、概述
在雙流等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),全網(wǎng)整合營銷推廣,外貿(mào)營銷網(wǎng)站建設(shè),雙流網(wǎng)站建設(shè)費(fèi)用合理。Java虛擬機(jī)規(guī)范規(guī)定的java虛擬機(jī)內(nèi)存其實(shí)就是java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū),其架構(gòu)如下:
其中方法區(qū)和堆是由所有線程共享的數(shù)據(jù)區(qū)。
虛擬機(jī)棧,本地方法棧和程序計(jì)數(shù)器是線程隔離的數(shù)據(jù)區(qū)。
二、詳解
下面來具體介紹這幾個(gè)數(shù)據(jù)區(qū)。
1、程序計(jì)數(shù)器
程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,其作用可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解析器工作時(shí)通過改變程序計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令。程序的分支、循環(huán)、跳轉(zhuǎn)、異常處理以及線程恢復(fù)等基礎(chǔ)功能都是依賴程序計(jì)數(shù)器來完成。
Java虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間片來實(shí)現(xiàn),在任何一個(gè)時(shí)刻,一個(gè)處理器只會執(zhí)行一條線程指令。因此,為了確保線程切換之后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要一個(gè)獨(dú)立的程序計(jì)數(shù)器,因此程序計(jì)數(shù)器是線程私有的內(nèi)存。
如果線程正在執(zhí)行一個(gè)java方法,則計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址。如果正在執(zhí)行的是Native方法( 簡單地講,一個(gè)Native Method就是一個(gè)java調(diào)用非java代碼的接口。該方法的實(shí)現(xiàn)由非java語言實(shí)現(xiàn),比如C。這個(gè)特征并非java所特有,很多其它的編程語言都有這一機(jī)制,比如在C++中,你可以用extern "C"告知C++編譯器去調(diào)用一個(gè)C的函數(shù)。),則計(jì)數(shù)器的值為空。程序計(jì)數(shù)器是java虛擬機(jī)中唯一一個(gè)沒有規(guī)定任何內(nèi)存溢出OutOfMemoryError情況的內(nèi)存區(qū)域。
2、java虛擬機(jī)棧
Java虛擬機(jī)棧也是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是java方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行時(shí)都會同時(shí)創(chuàng)建一個(gè)棧幀用于存放局部變量表、操作數(shù)棧、動態(tài)連接和方法出口等信息。每個(gè)方法被調(diào)用直至執(zhí)行完成過程,就對應(yīng)著一個(gè)棧幀在虛擬機(jī)中從入棧到出棧的過程。
Java虛擬機(jī)棧的局部變量表存放了編譯器可知的8種java基本類型數(shù)據(jù)(boolean、byte、char、short、int、float、long、double)、對象引用refrence(注意不是對象實(shí)例本身)、方法返回地址returnAddress(指向了一條字節(jié)碼指令的地址)。
Java虛擬機(jī)棧的局部變量表空間單位是槽(Slot),其中64位長度的double和long類型會占用兩個(gè)slot,其余的數(shù)據(jù)類型只占用一個(gè)slot。局部變量表所需內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí),該方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會改變局部變量表的大小。
Java虛擬機(jī)棧有兩種異常狀況:如果線程請求的棧深度大于虛擬機(jī)所允許的大深度時(shí),拋出StackOverflowError異常;如果虛擬機(jī)??梢詣討B(tài)擴(kuò)展,當(dāng)擴(kuò)展時(shí)無法申請到足夠內(nèi)存時(shí)會拋出OutOfMemoryError異常。
3、本地方法棧
本地方法棧與java虛擬機(jī)棧作用非常類似,其區(qū)別是:java虛擬機(jī)棧是為虛擬機(jī)執(zhí)行java方法服務(wù),而本地方法棧是為虛擬機(jī)調(diào)用的操作系統(tǒng)本地方法服務(wù)(如Native方法)。
Java虛擬機(jī)規(guī)范沒有對本地方法棧的實(shí)現(xiàn)和數(shù)據(jù)結(jié)構(gòu)做強(qiáng)制規(guī)定,Sun HotSpot虛擬機(jī)直接把java虛擬機(jī)棧和本地方法棧合二為一。
與java虛擬機(jī)棧類似,本地方法棧也會拋出StackOverflowError異常和OutOfMemoryError異常。
4、堆
堆是java虛擬機(jī)所管理的內(nèi)存區(qū)域中大一塊,java堆是被所有線程所共享的一塊內(nèi)存區(qū)域,在java虛擬機(jī)啟動時(shí)創(chuàng)建,堆內(nèi)存的唯一目的就是存放對象實(shí)例。幾乎所有的對象實(shí)例(包括數(shù)組)都是在堆分配內(nèi)存。
Java堆是垃圾收集器管理的主要區(qū)域,從垃圾回收的角度看,由于現(xiàn)在的垃圾收集器基本都采用的是分代收集算法,因此java堆還可以初步細(xì)分為新生代和年老代。
Java虛擬機(jī)規(guī)范規(guī)定,堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上是連續(xù)的即可。在實(shí)現(xiàn)上即可以是固定大小的,也可以是可動態(tài)擴(kuò)展的。如果在堆中沒有內(nèi)存完成實(shí)例分配,并且堆大小也無法在擴(kuò)展時(shí),將會拋出OutOfMemoryError異常。
堆設(shè)置的常用參數(shù)如下:??
-Xms :初始堆大小??
-Xmx :大堆大小,當(dāng)Xms和Xmx設(shè)置相同時(shí),堆就無法進(jìn)行自動擴(kuò)展。
-XX:NewSize=n :設(shè)置年輕代大小??
-XX:NewRatio=n: 設(shè)置年輕代和年老代的比值。如:為3,表示年輕代與年老代比值為1:3,年輕代占整個(gè)年輕代年老代和的1/4??
-XX:SurvivorRatio=n :年輕代中Eden區(qū)與Survivor區(qū)的比值。注意Survivor區(qū)有兩個(gè)。如:XX:SurvivorRatio=3,表示Eden區(qū)的大小是一個(gè)Survivor區(qū)的三倍,但Survivor區(qū)有兩個(gè),那么Eden:Survivor=3:2,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/5。?
-XX:MaxPermSize=n :設(shè)置持久代大小??
5、方法區(qū)
方法區(qū)與堆一樣,是被各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯后的代碼等數(shù)據(jù)。雖然java虛擬機(jī)規(guī)范把方法區(qū)描述為堆的一個(gè)邏輯部分,但是方法區(qū)卻有一個(gè)別名叫Non-Heap(非堆)。
Sun HotSpot虛擬機(jī)把方法區(qū)叫永久代(Permanent Generation),方法區(qū)中最重要的部分是運(yùn)行時(shí)常量池。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池,用于存放編譯期生成的各種字面變量、符號引用、直接引用等,這些內(nèi)容將在類加載后存放到方法區(qū)的運(yùn)行時(shí)常量池中,另外在運(yùn)行期間也可以將新的常量存放到常量池中,如String的intern()方法。當(dāng)調(diào)用 intern 方法時(shí),如果常量池已經(jīng)包含一個(gè)等于此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。否則,將此 String 對象添加到池中,并且返回此 String 對象的引用。
方法區(qū)和運(yùn)行時(shí)常量池在無法滿足內(nèi)存分配時(shí),也會拋出OutOfMemoryError異常。
6、直接內(nèi)存
直接內(nèi)存并不是java虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,也不是java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域,但是在java開發(fā)中還是會使用到。
JDK1.4中新引入的NIO(new I/O),引入了一種基于通道(Channel)和緩沖區(qū)(Buffer)的I/O方式,可以使用操作系統(tǒng)本地方法庫直接分配堆外內(nèi)存,然后通過一個(gè)存儲在java堆里面的DirectByteBuffer對象作為堆外直接內(nèi)存的引用進(jìn)行操作,避免了java堆內(nèi)存和本地直接內(nèi)存間的數(shù)據(jù)拷貝,可以顯著提高性能。
雖然直接內(nèi)存并不直接收到j(luò)ava虛擬機(jī)內(nèi)存影響,但是如果java虛擬機(jī)各個(gè)內(nèi)存區(qū)域總和大于物理內(nèi)存限制,從而導(dǎo)致直接內(nèi)存不足,動態(tài)擴(kuò)展時(shí)也會拋出OutOfMemoryError異常。
三、實(shí)例演示
下面通過簡單的小例子程序,演示Java虛擬機(jī)各部分內(nèi)存溢出情況:
1、java堆溢出
Java堆用于存儲實(shí)例對象,只要不斷創(chuàng)建對象,并且保證GC Roots到對象之間有引用的可達(dá),來避免垃圾收集器回收實(shí)例對象,就會在對象數(shù)量達(dá)到堆大容量時(shí)產(chǎn)生OutOfMemoryError異常。
想要方便快速地產(chǎn)生堆溢出,要使用如下java虛擬機(jī)參數(shù):-Xms=10m(最小堆內(nèi)存為10MB),-Xmx=10m(大堆內(nèi)存為10MB,最小堆內(nèi)存和大堆內(nèi)存相同是為了避免堆動態(tài)擴(kuò)展),-XX:+HeapDumpOnOutOfMemoryError可以讓java虛擬機(jī)在出現(xiàn)內(nèi)存溢出時(shí)產(chǎn)生當(dāng)前堆內(nèi)存快照以便進(jìn)行異常分析。
例子代碼如下:
public class HeapOOM{??
? static class OOMObject{??
}??
public static void main(String[] args){??
? List<OOMObject> list = new ArrayList<OOMObject>();??
? while(true){??
? list.add(new OOMObject());??
}??
}??
}??
運(yùn)行一段時(shí)間就會發(fā)現(xiàn)產(chǎn)生OutOfMemoryError異常,并且產(chǎn)生了堆內(nèi)存異常dump文件。
2、java虛擬機(jī)棧和本地方法棧溢出
由于Sun的HotSpot虛擬機(jī)不區(qū)分java虛擬機(jī)棧和本地方法棧,因此對于HotSpot虛擬機(jī)來說-Xoss參數(shù)(設(shè)置本地方法棧大小)雖然存在,但是實(shí)際上是無效的,棧容量只能由-Xss參數(shù)設(shè)定。
由于Java虛擬機(jī)棧會出現(xiàn)StackOverflowError和OutOfMemoryError兩種異常,所以分別使用兩個(gè)例子演示這兩種情況:
(1)java虛擬機(jī)棧深度溢出
單線程的環(huán)境下,無論是由于棧幀太大,還是虛擬機(jī)棧容量太小,當(dāng)內(nèi)存無法再分配的時(shí)候,虛擬機(jī)總拋出StackOverflowError異常。使用-Xss128k將java虛擬機(jī)棧大小設(shè)置為128kb,例子代碼如下:
public class JavaVMStackOF{??
? private int stackLength = 1;??
? public void stackLeak(){??
? statckLength++;??
? stackLeak();??
}??
public static void main(String[] args){??
? JavaVMStackOF oom = new JavaVMStackOF();??
oom.stackLeak();??
}??
}??
運(yùn)行一段時(shí)間后,產(chǎn)生StackOverflowError異常。Java虛擬機(jī)棧溢出一般會產(chǎn)生在方法遞歸調(diào)用過多而java虛擬機(jī)棧內(nèi)存不夠的情況下。
(2)java虛擬機(jī)棧內(nèi)存溢出
多線程環(huán)境下,能夠創(chuàng)建的線程大內(nèi)存=物理內(nèi)存-大堆內(nèi)存-大方法區(qū)內(nèi)存,在java虛擬機(jī)棧內(nèi)存一定的情況下,單個(gè)線程占用的內(nèi)存越大,所能創(chuàng)建的線程數(shù)目越小,所以在多線程條件下很容易產(chǎn)生java虛擬機(jī)棧內(nèi)存溢出的異常。
使用-Xss2m參數(shù)設(shè)置java虛擬機(jī)棧內(nèi)存大小為2MB,例子代碼如下:
public class JavaVMStackOOM{??
? private void dontStop(){??
? while(true){??
}??
}??
public void stackLeakByThread(){??
? while(true){??
? Thread t = new Thread(new Runnable(){??
? public void run(){??
? dontStop();??
}??
});??
t.start();??
}??
}? ?
public static void main(String[] args){??
? JavaVMStackOOM oom = new JavaVMStackOOM();??
? oom. stackLeakByThread();.??
}??
}??
運(yùn)行一段時(shí)間之后,java虛擬機(jī)棧就會因?yàn)閮?nèi)存太小無法創(chuàng)建線程而產(chǎn)生OutOfMemoryError。
3、運(yùn)行時(shí)常量池溢出
運(yùn)行時(shí)常量池屬于方法區(qū)的一部分,可以使用-XX:PermSize=10m和-XX:MaxPermSize=10m將永久代大內(nèi)存和最小內(nèi)存設(shè)置為10MB大小,并且由于永久代大內(nèi)存和最小內(nèi)存大小相同,因此無法擴(kuò)展。
String的intern()方法用于檢查常量池中如果有等于此String對象的字符串存在,則直接返回常量池中的字符串對象,否則,將此String對象所包含的字符串添加到運(yùn)行時(shí)常量池中,并返回此String對象的引用。因此String的intern()方法特別適合演示運(yùn)行時(shí)常量池溢出,例子代碼如下:
public class RuntimeConstantPoolOOM{??
? public static void main(String[] args){??
List<String> list = new ArrayList<String>();??
? int i = 0;??
? while(true){??
? list.add(String.valueOf(i++).intern());??
}??
}??
}??
運(yùn)行一段時(shí)間,永久代內(nèi)存不夠,運(yùn)行時(shí)常量池因無法再添加常量而產(chǎn)生OutOfMemoryError。
4、方法區(qū)溢出
運(yùn)行時(shí)常量池是方法區(qū)的一部分,他們都屬于HotSpot虛擬機(jī)中的永久代內(nèi)存區(qū)域。方法區(qū)用于存放Class的相關(guān)信息,Java的反射和動態(tài)代理可以動態(tài)產(chǎn)生Class,另外第三方的CGLIB可以直接操作字節(jié)碼,也可以動態(tài)產(chǎn)生Class,實(shí)驗(yàn)通過CGLIB來演示,同樣使用-XX:PermSize=10m和-XX:MaxPermSize=10m將永久代大內(nèi)存和最小內(nèi)存設(shè)置為10MB大小,并且由于永久代大內(nèi)存和最小內(nèi)存大小相同,因此無法擴(kuò)展。例子代碼如下:
public class JavaMethodAreaOOM{??
? public static void main(String[] args){??
? while(true){??
? Enhancer enhancer = new Enhancer();??
? enhancer.setSuperClass(OOMObject.class);??
? enhancer.setUseCache(false);??
? enhancer.setCallback(new MethodInterceptor(){??
? public Object intercept(Object obj, Method method, Object[] args,? ?
MethodProxy proxy)throws Throwable{??
? return proxy.invokeSuper(obj, args);??
}??
});??
enhancer.create();??
}??
}??
class OOMObject{??
}? ?
}??
運(yùn)行一段時(shí)間之后,永久代內(nèi)存不夠,方法區(qū)無法再存放CGLIB創(chuàng)建處理的Class信息,產(chǎn)生方法區(qū)OutOfMemoryError。
5、本機(jī)直接內(nèi)存溢出
Java虛擬機(jī)可以通過參數(shù)-XX:MaxDirectMemorySize設(shè)定本機(jī)直接內(nèi)存可用大小,如果不指定,則默認(rèn)與java堆內(nèi)存大小相同。JDK中可以通過反射獲取Unsafe類(Unsafe的getUnsafe()方法只有啟動類加載器Bootstrap才能返回實(shí)例)直接操作本機(jī)直接內(nèi)存。通過使用-XX:MaxDirectMemorySize=10M,限制大可使用的本機(jī)直接內(nèi)存大小為10MB,例子代碼如下:
public class DirectMemoryOOM{??
? private static final int _1MB = 1024* 1024 * 1024;??
? publc static void main(String[] args) throws Exception{??
? Field unsafeField = Unsafe.class.getDeclaredFields()[0];??
? unsafeField.setAccessible(true);??
? Unsafe unsafe = (Unsafe) unsafeField.get(null);??
? while(true){??
? //unsafe直接想操作系統(tǒng)申請內(nèi)存??
? unsafe.allocateMemory(_1MB);??
}??
}??
}??
當(dāng)運(yùn)行一段時(shí)間之后,10MB的本機(jī)直接內(nèi)存被分配光,無法在進(jìn)行直接內(nèi)存分配時(shí),產(chǎn)生OutOfMemoryError。
三、總結(jié)
java虛擬機(jī)內(nèi)存結(jié)構(gòu)中的程序計(jì)數(shù)器、虛擬機(jī)棧和本地方法棧這三個(gè)區(qū)域隨線程創(chuàng)建而生,隨線程銷毀而滅,因此這三個(gè)區(qū)域的內(nèi)存分配和回收是確定的,java垃圾收集器重點(diǎn)關(guān)注的是java虛擬機(jī)的堆內(nèi)存和方法區(qū)內(nèi)存。
歡迎加入Java技術(shù)交流群:659270626
群內(nèi)提供免費(fèi)的學(xué)習(xí)指導(dǎo),提供Spring源碼、MyBatis、Netty、Redis,Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx、分布式、高并發(fā)、性能調(diào)優(yōu)等架構(gòu)技術(shù)架構(gòu)資料以及免費(fèi)的解答
不懂的問題都可以在本群提出來,之后還會有職業(yè)生涯規(guī)劃以及面試指導(dǎo) 。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。
網(wǎng)頁題目:java虛擬機(jī)中的內(nèi)存區(qū)域劃分-創(chuàng)新互聯(lián)
本文鏈接:http://jinyejixie.com/article48/dhoehp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、品牌網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作、企業(yè)建站、動態(tài)網(wǎng)站、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容