java一个接口执行结束释放内存_java的灵魂--JVM虚拟机
JVM是運(yùn)行在操作系統(tǒng)之上的,它與硬件沒(méi)有直接的交互
JVM體系結(jié)構(gòu)
1.類加載器
負(fù)責(zé)加載class文件,class文件在文件開(kāi)頭有特定的文件標(biāo)示, 并且ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運(yùn)行,則由 Execution Engine 執(zhí)行引擎決定,Execution Engine執(zhí)行引擎負(fù)責(zé)解釋命令,提 交操作系統(tǒng)執(zhí)行。
類加載器的分類 (四種)
虛擬機(jī)自帶的類加載器(三種)
啟動(dòng)類加載器(c++)
擴(kuò)展類加載器(java)
應(yīng)用程序加載器(java)
用戶自定義加載器()
Java.lang.ClassLoader的子類,用 戶可以定制類的加載方式
2.本地方法接口
調(diào)用本地系統(tǒng)的接口,用來(lái)驅(qū)動(dòng)硬件
3.Native Method Stack(本地方法棧)
它的具體做法是Native Method Stack中登記native方法,在Execution Engine 執(zhí)行時(shí)加載本地方法庫(kù)。
4.java 棧(實(shí)例方法,引用,基本數(shù)據(jù)類型)
棧 也叫棧內(nèi)存,主管java程序的運(yùn)行
是在線程創(chuàng)建時(shí)創(chuàng)建,它 的生命期是跟隨線程的生命期,線程結(jié)束棧內(nèi)存也就釋放,對(duì)于棧來(lái)說(shuō) 不存在垃圾回收問(wèn)題,只要線程一結(jié)束該棧就Over,生命周期和線程一 致,是線程私有的。8種基本類型的變量+對(duì)象的引用變量+實(shí)例方法都 是在函數(shù)的棧內(nèi)存中分配。
棧存儲(chǔ)什么?
棧幀中主要保存3 類數(shù)據(jù):
本地變量(Local Variables):輸入?yún)?shù)和輸出參數(shù)以及方法內(nèi)的變量;
棧操作(Operand Stack):記錄出棧、入棧的操作;
棧幀數(shù)據(jù)(Frame Data):包括類文件、方法等等。
棧運(yùn)行原理:
棧中的數(shù)據(jù)都是以棧幀(Stack Frame)的格式存在,棧幀是一個(gè)內(nèi)存區(qū) 塊,是一個(gè)數(shù)據(jù)集,是一個(gè)有關(guān)方法(Method)和運(yùn)行期數(shù)據(jù)的數(shù)據(jù)集, 當(dāng)一個(gè)方法A被調(diào)用時(shí)就產(chǎn)生了一個(gè)棧幀 F1,并被壓入到棧中, A方法又調(diào)用了 B方法,于是產(chǎn)生棧幀 F2 也被壓入棧, B方法又調(diào)用了 C方法,于是產(chǎn)生棧幀 F3 也被壓入棧, …… 執(zhí)行完畢后,先彈出F3棧幀,再?gòu)棾鯢2棧幀,再?gòu)棾鯢1棧幀…… 遵循“先進(jìn)后出”/“后進(jìn)先出”原則。
5.程序計(jì)數(shù)器
每個(gè)線程都有一個(gè)程序計(jì)數(shù)器,是線程私有的,就是一個(gè)指針, 指向方法區(qū)中的方法字節(jié)碼(用來(lái)存儲(chǔ)指向下一條指令的地址,也即將 要執(zhí)行的指令代碼),由執(zhí)行引擎讀取下一條指令,是一個(gè)非常小的內(nèi) 存空間,幾乎可以忽略不記。
6.方法區(qū)
方法區(qū)是被所有線程共享,所有字段和方法字節(jié)碼,以及一些特 殊方法如構(gòu)造函數(shù),接口代碼也在此定義。簡(jiǎn)單說(shuō),所有定義的方法的信 息都保存在該區(qū)域,此區(qū)屬于共享區(qū)間。 靜態(tài)變量+常量+類信息(構(gòu)造方法/接口定義)+運(yùn)行時(shí)常量池存在方法區(qū)中 But 實(shí)例變量存在堆內(nèi)存中,和方法區(qū)無(wú)關(guān)
7.Heap 堆
一個(gè)JVM實(shí)例只存在一個(gè)堆內(nèi)存,堆內(nèi)存的大小是可以調(diào)節(jié)的。 類加載器讀取了類文件后,需要把類、方法、常變量放到堆內(nèi)存中,保 存所有引用類型的真實(shí)信息,以方便執(zhí)行器執(zhí)行,進(jìn)一步劃分的目的是更好地回收內(nèi)存,或者更快地分配內(nèi)存,堆內(nèi)存分為三部分:
Young Generation Space 新生區(qū) Young/New
Tenure generation space 養(yǎng)老區(qū) Old/ Tenure
Permanent Space 永久區(qū) Perm
圖所示的 eden 區(qū)、s0 區(qū)、s1 區(qū)都屬于新生代,tentired 區(qū)屬于老年代。大部分情況,對(duì)象都會(huì)首先在 Eden 區(qū)域分配,在一次新生代垃圾回收后,如果對(duì)象還存活,則會(huì)進(jìn)入 s0 或者 s1,并且對(duì)象的年齡還會(huì)加 1(Eden 區(qū)->Survivor 區(qū)后對(duì)象的初始年齡變?yōu)?1),當(dāng)它的年齡增加到一定程度(默認(rèn)為 15 歲),就會(huì)被晉升到老年代中。對(duì)象晉升到老年代的年齡閾值,可以通過(guò)參數(shù)-XX:MaxTenuringThreshold 來(lái)設(shè)置。
新生區(qū) 新生區(qū)是類的誕生、成長(zhǎng)、消亡的區(qū)域,一個(gè)類在這里產(chǎn)生,應(yīng)用,最后被垃圾 回收器收集,結(jié)束生命。新生區(qū)又分為兩部分: 伊甸區(qū)(Eden space)和幸存者區(qū) (Survivor pace) ,所有的類都是在伊甸區(qū)被new出來(lái)的。幸存區(qū)有兩個(gè): 0區(qū) (Survivor 0 space)和1區(qū)(Survivor 1 space)。當(dāng)伊甸園的空間用完時(shí),程序又需要 創(chuàng)建對(duì)象,JVM的垃圾回收器將對(duì)伊甸園區(qū)進(jìn)行垃圾回收(Minor GC),將伊甸園區(qū)中的不 再被其他對(duì)象所引用的對(duì)象進(jìn)行銷毀。然后將伊甸園中的剩余對(duì)象移動(dòng)到幸存 0區(qū)。若幸 存 0區(qū)也滿了,再對(duì)該區(qū)進(jìn)行垃圾回收,然后移動(dòng)到 1 區(qū)。那如果1 區(qū)也滿了呢?再次垃 圾回收,滿足條件后(默認(rèn)15歲)再移動(dòng)到養(yǎng)老區(qū)。若養(yǎng)老區(qū)也滿了,那么這個(gè)時(shí)候?qū)a(chǎn)生MajorGC (FullGC),進(jìn)行養(yǎng)老區(qū)的內(nèi)存清理。若養(yǎng)老區(qū)執(zhí)行了Full GC之后發(fā)現(xiàn)依然無(wú)法進(jìn)行對(duì)象 的保存,就會(huì)產(chǎn)生OOM異常“OutOfMemoryError”。
如果出現(xiàn)java.lang.OutOfMemoryError: Java heap space異常,說(shuō)明 Java虛擬機(jī)的堆內(nèi)存不夠。原因有二:
(1)Java虛擬機(jī)的堆內(nèi)存設(shè)置不夠,可以通過(guò)參數(shù)-Xms、-Xmx來(lái)調(diào)整。
(2)代碼中創(chuàng)建了大量大對(duì)象,并且長(zhǎng)時(shí)間不能被垃圾收集器收集(存 在被引用)。
永久區(qū) 永久存儲(chǔ)區(qū)是一個(gè)常駐內(nèi)存區(qū)域,用于存放JDK自身所攜帶的 Class,Interface 的元數(shù)據(jù),也就是說(shuō)它存儲(chǔ)的是運(yùn)行環(huán)境必須的類信息,被裝 載進(jìn)此區(qū)域的數(shù)據(jù)是不會(huì)被垃圾回收器回收掉的,關(guān)閉 JVM 才會(huì)釋放此區(qū)域所 占用的內(nèi)存。
常量池(Constant Pool)是方法區(qū)的一部分,Class文件除了有類的版本、字段、方法、 接口等描述信息外,還有一項(xiàng)信息就是常量池,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常 量池中存放。
堆數(shù)據(jù)調(diào)優(yōu)簡(jiǎn)介
-Xms1024m -Xmx1024m -XX:+PrintGCDetails
方法區(qū)和永久代的關(guān)系
《Java 虛擬機(jī)規(guī)范》只是規(guī)定了有方法區(qū)這么個(gè)概念和它的作用,并沒(méi)有規(guī)定如何去實(shí)現(xiàn)它。那么,在不同的 JVM 上方法區(qū)的實(shí)現(xiàn)肯定是不同的了。 方法區(qū)和永久代的關(guān)系很像 Java 中接口和類的關(guān)系,類實(shí)現(xiàn)了接口,而永久代就是 HotSpot 虛擬機(jī)對(duì)虛擬機(jī)規(guī)范中方法區(qū)的一種實(shí)現(xiàn)方式。 也就是說(shuō),永久代是 HotSpot 的概念,方法區(qū)是 Java 虛擬機(jī)規(guī)范中的定義,是一種規(guī)范,而永久代是一種實(shí)現(xiàn),一個(gè)是標(biāo)準(zhǔn)一個(gè)是實(shí)現(xiàn),其他的虛擬機(jī)實(shí)現(xiàn)并沒(méi)有永久帶這一說(shuō)法。
為什么要將永久代 (PermGen) 替換為元空間 (MetaSpace) 呢?
整個(gè)永久代有一個(gè) JVM 本身設(shè)置固定大小上線,無(wú)法進(jìn)行調(diào)整,而元空間使用的是直接內(nèi)存,受本機(jī)可用內(nèi)存的限制,并且永遠(yuǎn)不會(huì)得到 java.lang.OutOfMemoryError。你可以使用 -XX:MaxMetaspaceSize 標(biāo)志設(shè)置最大元空間大小,默認(rèn)值為 unlimited,這意味著它只受系統(tǒng)內(nèi)存的限制。-XX:MetaspaceSize 調(diào)整標(biāo)志定義元空間的初始大小如果未指定此標(biāo)志,則 Metaspace 將根據(jù)運(yùn)行時(shí)的應(yīng)用程序需求動(dòng)態(tài)地重新調(diào)整大小。
當(dāng)然這只是其中一個(gè)原因,還有很多底層的原因,這里就不提了。
String 類和常量池
String 對(duì)象的兩種創(chuàng)建方式:
String str1 = "abcd";//先檢查字符串常量池中有沒(méi)有"abcd",如果字符串常量池中沒(méi)有,則創(chuàng)建一個(gè),然后 str1 指向字符串常量池中的對(duì)象,如果有,則直接將 str1 指向"abcd""; String str2 = new String("abcd");//堆中創(chuàng)建一個(gè)新的對(duì)象 String str3 = new String("abcd");//堆中創(chuàng)建一個(gè)新的對(duì)象 System.out.println(str1==str2);//false System.out.println(str2==str3);//false
第一種方式是在常量池中拿對(duì)象;
第二種方式是直接在堆內(nèi)存空間創(chuàng)建一個(gè)新的對(duì)象。
String 類型的常量池比較特殊。它的主要使用方法有兩種:
直接使用雙引號(hào)聲明出來(lái)的 String 對(duì)象會(huì)直接存儲(chǔ)在常量池中。
如果不是用雙引號(hào)聲明的 String 對(duì)象,可以使用 String 提供的 intern 方法。String.intern() 是一個(gè) Native 方法,它的作用是:如果運(yùn)行時(shí)常量池中已經(jīng)包含一個(gè)等于此 String 對(duì)象內(nèi)容的字符串,則返回常量池中該字符串的引用;如果沒(méi)有,則在常量池中創(chuàng)建與此 String 內(nèi)容相同的字符串,并返回常量池中創(chuàng)建的字符串的引用。
String s1 = new String("計(jì)算機(jī)"); String s2 = s1.intern(); String s3 = "計(jì)算機(jī)"; System.out.println(s2);//計(jì)算機(jī) System.out.println(s1 == s2);//false,因?yàn)橐粋€(gè)是堆內(nèi)存中的 String 對(duì)象一個(gè)是常量池中的 String 對(duì)象, System.out.println(s3 == s2);//true,因?yàn)閮蓚€(gè)都是常量池中的 String 對(duì)象
字符串拼接:
String str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing";//常量池中的對(duì)象 String str4 = str1 + str2; //在堆上創(chuàng)建的新的對(duì)象 String str5 = "string";//常量池中的對(duì)象 System.out.println(str3 == str4);//false System.out.println(str3 == str5);//true System.out.println(str4 == str5);//false
String s1 = new String("abc");這句話創(chuàng)建了幾個(gè)字符串對(duì)象?
將創(chuàng)建 1 或 2 個(gè)字符串。如果池中已存在字符串文字“abc”,則池中只會(huì)創(chuàng)建一個(gè)字符串“s1”。如果池中沒(méi)有字符串文字“abc”,那么它將首先在池中創(chuàng)建,然后在堆空間中創(chuàng)建,因此將創(chuàng)建總共 2 個(gè)字符串對(duì)象。
驗(yàn)證:
String s1 = new String("abc");// 堆內(nèi)存的地址值 String s2 = "abc"; System.out.println(s1 == s2);// 輸出 false,因?yàn)橐粋€(gè)是堆內(nèi)存,一個(gè)是常量池的內(nèi)存,故兩者是不同的。 System.out.println(s1.equals(s2));// 輸出 true
4.3 8 種基本類型的包裝類和常量池
Java 基本類型的包裝類的大部分都實(shí)現(xiàn)了常量池技術(shù),即 Byte,Short,Integer,Long,Character,Boolean;這 5 種包裝類默認(rèn)創(chuàng)建了數(shù)值[-128,127] 的相應(yīng)類型的緩存數(shù)據(jù),但是超出此范圍仍然會(huì)去創(chuàng)建新的對(duì)象。
兩種浮點(diǎn)數(shù)類型的包裝類 Float,Double 并沒(méi)有實(shí)現(xiàn)常量池技術(shù)。
Integer i1 = 33; Integer i2 = 33; System.out.println(i1 == i2);// 輸出 true Integer i11 = 333; Integer i22 = 333; System.out.println(i11 == i22);// 輸出 false Double i3 = 1.2; Double i4 = 1.2; System.out.println(i3 == i4);// 輸出 false
棧溢出怎么定義解決:
第一對(duì)所有的代碼包括頁(yè)面中的java代碼都進(jìn)行一遍徹底的回顧檢查, 1.對(duì)那些靜態(tài)(static)的對(duì)象要特別留神,特別是類型為Map,List,Set的,靜態(tài)的變量會(huì)一直駐存在內(nèi)存中,生命周期比較長(zhǎng),不會(huì)被垃圾器回收。 2.對(duì)于代碼,要審查是否生成了大量的冗余的對(duì)象,還有一些邏輯業(yè)務(wù)處理的類, 算法是否過(guò)于復(fù)雜,調(diào)整算法,對(duì)于代碼認(rèn)真審查,再仔細(xì)重構(gòu)一遍代碼,能提高代碼質(zhì)量,提高程序運(yùn)行穩(wěn)定性。 3.Java中的內(nèi)存溢出大都是因?yàn)闂V械淖兞刻嗔恕F鋵?shí)內(nèi)存有的是。建議不用的盡量設(shè)成null以便回收,多用局部變量,少用成員變量。 1),變量所包含的對(duì)象體積較大,占用內(nèi)存較多。 2),變量所包含的對(duì)象生命周期較長(zhǎng)。 3),變量所包含的對(duì)象數(shù)據(jù)穩(wěn)定。 4),該類的對(duì)象實(shí)例有對(duì)該變量所包含的對(duì)象的共享需求。 4.在我的程序中對(duì)靜態(tài)變量的優(yōu)化后,使程序占用內(nèi)存量至少提升了5k-10k。所以也不容忽視。 第二還有就是String類相關(guān)的東西: 1.字符串累加的時(shí)候一定要用StringBuffer的append方法,不要使用+操作符連接兩個(gè)字符串。差別很大。而且在循環(huán)或某些重復(fù)執(zhí)行的動(dòng)作中不要去創(chuàng)建String對(duì)象,因?yàn)镾tring對(duì)象是要用StringBuffer對(duì)象來(lái)處理的,一個(gè)String對(duì)象應(yīng)該是產(chǎn)生了 3個(gè)對(duì)象(大概是這樣:))。 2.字符串length()方法來(lái)取得字符串長(zhǎng)度的時(shí)候不要把length放到循環(huán)中,可以在循環(huán)外面對(duì)其取值。(包括vector的size方法)。特別是循環(huán)次數(shù)多的時(shí)候,盡量把length放到循環(huán)外面。 int size = xmlVector.size(); for (int i = 2; i < size; i++) { 。。。 } 3 寫代碼的時(shí)候處理內(nèi)存溢出 try{ //do sth .... }catch (outofmemoryerror e){//可以用一個(gè)共通函數(shù)來(lái)執(zhí)行. system.out.print (“no memory! ”); system.gc(); //do sth again .... } 4.對(duì)于頻繁申請(qǐng)內(nèi)存和釋放內(nèi)存的操作,還是自己控制一下比較好,但是System.gc()的方法不一定適用,最好使用finallize強(qiáng)制執(zhí)行或者寫自己的finallize方法。 Java 中并不保證每次調(diào)用該方法就一定能夠啟動(dòng)垃圾收集,它只不過(guò)會(huì)向JVM發(fā)出這樣一個(gè)申請(qǐng),到底是否真正執(zhí)行垃圾收集,一切都是個(gè)未知數(shù)。
5.出現(xiàn)了循環(huán)遞歸調(diào)用
總結(jié)
以上是生活随笔為你收集整理的java一个接口执行结束释放内存_java的灵魂--JVM虚拟机的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mac redis 链接_在Ubunt/
- 下一篇: 工业机器人电路图讲解话术_燃气传感器技术