楊靜,楊觀賜
(貴州大學(xué) 現(xiàn)代制造技術(shù)教育部重點實驗室,貴陽 550000)
?
基于Java程序的內(nèi)存空間布局規(guī)則研究*
楊靜,楊觀賜
(貴州大學(xué) 現(xiàn)代制造技術(shù)教育部重點實驗室,貴陽 550000)
針對在一定大小的內(nèi)存空間中Jave虛擬機在處理大型Jave程序時,Jave對象之間頻繁交互導(dǎo)致內(nèi)存占用高、處理效率低的問題,給出了減少Jave對象占用內(nèi)存空間的三種布局規(guī)則。該規(guī)則利用Jave虛擬機運行機制,綜合對象屬性及影響內(nèi)存空間大小等指標(biāo),得出相同對象不同屬性之間按規(guī)則存放順序的最優(yōu)方法。結(jié)果表明,相對傳統(tǒng)的相同對象不同屬性之間無規(guī)則的存放方法,按規(guī)則存放順序的方法能夠大幅度節(jié)省內(nèi)存空間,并有效提高Jave虛擬機的運行效率,程序?qū)ο笤蕉啵摲椒▽?nèi)存空間的節(jié)省和計算效率的提高效果就越明顯。
Jave程序;內(nèi)存空間;布局規(guī)則;屬性;對象
隨著社會經(jīng)濟的迅猛發(fā)展與市場對軟件性能要求的提高,基于Java的應(yīng)用程序因其具有“一次編譯,到處運行”的特點而得到廣泛應(yīng)用。雖然Java開發(fā)人員對JDK虛擬機解釋器不斷進行優(yōu)化處理,但是仍然不能完全令用戶滿意。目前國內(nèi)Java研究者主要將焦點放在提高現(xiàn)有的開發(fā)設(shè)備,但是面對大型的Java程序時,效果并不明顯[1]。參考文獻[2]中提出在Java虛擬機中使用垃圾回收機制。在虛擬監(jiān)控之下,垃圾收集器將定期進行清除行動,當(dāng)所有對象是非常穩(wěn)定的,此時垃圾回收器的效率會非常低,切換到“標(biāo)記-掃描”模式,當(dāng)堆空間存在很多碎片,切換到“標(biāo)記-清理”模式,但如果想撤銷內(nèi)存之外的清理工作,就只能調(diào)用Java的某個方法,這樣做只是對虛擬機中產(chǎn)生的垃圾進行清理,從而減少內(nèi)存占用。參考文獻[3]介紹了Java的多線程機制的應(yīng)用,從而減少內(nèi)存消耗,提高程序運行速度,但是Java的多線程機制容易造成死鎖和資源分配不均等問題。參考文獻[4]采用三種基于硬件的代碼緩存策略,采用動態(tài)方式寫入和讀取Java代碼,這種方式過于依賴CPU執(zhí)行性能。參考文獻[5]主要研究了內(nèi)存池空間調(diào)度數(shù)據(jù)的研究,通過內(nèi)存池解決了內(nèi)外存之間頻繁的交付問題。參考文獻[6]、[7]介紹了一種Java虛擬機將類的全限定名分離為不同的結(jié)點,減少整個類的字符串在常量池中所占據(jù)的大小,這使得在內(nèi)存有限的系統(tǒng)中裝載.class文件后,能減少對存儲空間的占用。
本文主要從Java對象占用空間的角度分析,對Java對象空間做優(yōu)化處理,首先分析了Java虛擬機以及Java類的加載過程,其次研究了Java對象的性質(zhì),提出了Java對象的使用規(guī)則,從而達到降低內(nèi)存消耗,提高運行效率的目的。
Java虛擬機(Java virtual machine,JVM)是運行 Java 程序必不可少的機制。JVM實現(xiàn)了Java語言最重要的特征,即平臺無關(guān)性。其原理為編譯后的 Java 程序指令并不直接在硬件系統(tǒng)的 CPU 上執(zhí)行,而是由 JVM 執(zhí)行。JVM屏蔽了與具體平臺相關(guān)的信息,使Java語言編譯程序只需要生成在JVM上運行的目標(biāo)字節(jié)碼(.class),就可以在多種平臺上不加修改地運行。Java 虛擬機在執(zhí)行字節(jié)碼時,把字節(jié)碼解釋成具體平臺上的機器指令執(zhí)行,因此實現(xiàn)Java平臺的無關(guān)性。它是 Java 程序能在多平臺間進行無縫移植的可靠保證,同時也是 Java 程序的安全檢驗引擎(還進行安全檢查)。JVM 是 編譯后的Java 程序(.class文件)和硬件系統(tǒng)之間的接口(編譯后,Javac 是收錄于JDK中的Java 語言編譯器,該工具可以將后綴名為.Java的源文件編譯為后綴名為.class的可以運行于Java虛擬機的字節(jié)碼)。Java內(nèi)存空間工作原理如圖1所示。
圖1 Java內(nèi)存空間工作原理
JVM=類加載器classloader+執(zhí)行引擎execution engine+運行時數(shù)據(jù)區(qū)域runtime data area classloader,把硬盤上的.class文件加載到JVM中運行時數(shù)據(jù)區(qū)域,但是它不負責(zé)這個類文件能否執(zhí)行,這個是執(zhí)行引擎負責(zé)的[8]。classloader作用是裝載.class文件,classloader有兩種裝載.class的方式:第一種:隱式,運行過程中,碰到new方式生成對象,隱式調(diào)用classloader到JVM;第二種:顯示,通過class.forname()動態(tài)加載[9]。
類的加載過程采用雙親委托機制[10],這種機制能更好地保證Java平臺的安全。該模型要求除了頂層的Bootstrap Class Loader啟動類加載器外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器。子類加載器和父類加載器不是以繼承(interface)關(guān)系來復(fù)用父類加載器的代碼。每個類加載器都有自己的命名空間(由該加載器及所有父類加載器所加載的類組成,在同一個命名空間中,不會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類;在不同的命名空間中,有可能會出現(xiàn)類的完整名字(包括類的包名)相同的兩個類)。雙親委托機制具體的工作過程為:classloader首先從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載,則直接返回原來已經(jīng)加載的類。每個類加載器都有自己的加載緩存,當(dāng)一個類被加載了以后就會放入緩存,等下次加載的時候就可以直接返回。如果沒有找到被加載的類,則委托父類加載器去加載,父類加載器采用同樣的策略,首先查看自己的緩存,然后委托父類的父類去加載,一直到Bookstrap Class Loader。當(dāng)所有父類加載器都沒有加載的時候,再由當(dāng)前的類加載器加載,并將其放入它自己的緩存中,以便下次有加載請求的時候直接返回。使用這種模型來組織類加載器之間的關(guān)系,主要是為了安全性,避免用戶自己編寫的類動態(tài)替換Java的一些核心類(比如String),同時也避免了重復(fù)加載,因為JVM中區(qū)分不同類,不僅僅是根據(jù)類名,相同的.class文件被不同的classloader加載就是不同的兩個類,如果相互轉(zhuǎn)型的話,會拋Java.lang.ClassCaseException。類加載器[11]classloader是具有層次架構(gòu)的,也就是父子關(guān)系。其中,Boolstrap是所有類加載器的父親。具體關(guān)系如圖2所示。
圖2 Java類的加載過程
2.1 對象所占空間分析
Java對象內(nèi)存布局:對象頭(header)、實例數(shù)據(jù)(Instance Data)和對齊填充(padding)[12]。所在環(huán)境為32位的windows系統(tǒng),對象頭在32位系統(tǒng)中占用8字節(jié)。原生類型[13](Primitive Type)的內(nèi)存占用如表1所列。
表1 原生類型的內(nèi)存占用
Reference在32位系統(tǒng)上每個占用4字節(jié)。對齊填充,Hotspot的對齊方式為8字節(jié)對齊:(對象頭+實例數(shù)據(jù)+padding)%8=0,且0≤padding<8。首先,任何對象都是8字節(jié)對齊,對象占用內(nèi)存大小的計算公式為:
對象占用字節(jié)數(shù)=基本的8字節(jié)+基本數(shù)據(jù)類型所占用的內(nèi)存空間(累加后對齊到8的倍數(shù))+對象引用所占用的空間(累加后對齊到8的倍數(shù))
2.2 對象所占空間的分配規(guī)則
經(jīng)過前面對Java對象相關(guān)屬性分析以及Java虛擬機的工作原理和Java類的加載過程的分析研究,可以知道Sun公司的Jave虛擬機并沒有按照屬性聲明的順序來進行內(nèi)存布局,而是按照下面的順序規(guī)則來進行內(nèi)存布局:[long,double]、[int,float]、[char,short]、[byte,boolean]、reference(引用類型),這樣可以節(jié)約Java運行時的內(nèi)存空間。舉例如下:
Public class Test{
Byte a;
Boolean b;
Char c;
Short d;
Float e;
Double f;
Object g;
}
如果這個對象的屬性按照無規(guī)則的順序存放的話,要占用的空間為:head(8)+a(1)+b(1)+c(2)+d(2)+padding(2)+e(4)+padding(4)+f(8)+g(4)+padding(4)=40,但是按照這個規(guī)則得到:head(8)+f(8)+e(4)+d(2)+c(2)+a(1)+b(1)+e(4)+padding(2)=32,可以看到節(jié)省了不少空間。
2.3 Java繼承其他子類的內(nèi)存布局規(guī)則
Java虛擬機將遵循以下規(guī)則來組織父類中的類成員以及子類和父類中類成員的關(guān)系,規(guī)則如下:不同類繼承關(guān)系中的成員不能混合排列。首先按照規(guī)則1處理父類中的成員,接著才是子類的成員。舉例:
Class A{
Long a;
Int b;
Int c;
}
Class B extends A{
Long d;
}
這樣存放的順序及占用空間如下:head(8)+a(8)+b(4)+c(4)+d(8)=32。這是比較理想的情況,父類成員和子類成員剛好滿足對其的填充規(guī)則,但在實際編程中,如果父類中的屬性不夠8個字節(jié),就有了新的一條內(nèi)存布局規(guī)則:父類中最后一個成員與子類的第一個成員的間隔如果不夠4個字節(jié),此時需要擴展到4個字節(jié)的基本單位,舉例:
Class A{
Byte a;
}
Class B extends A{
Byte b;
}
那么此時占用的空間如下:head(8)+a(1)+padding(3)+b(1)+padding(3)=16,顯然這種方式比較浪費空間。
當(dāng)子類的第一個成員是Double或者Long,并且父類并沒有用完8個字節(jié),此時JVM會破壞規(guī)則,將較小的數(shù)據(jù)填充到該空間,舉例:
Class A{
Byte a;
}
Class B extends A{
Long b;
Short c;
Byte d;
}
此時占用的空間如下:head(8)+a(1)+padding(3)+c(2)+d(1)+padding(1)+b(8)=24。
2.4 內(nèi)存使用情況
針對Java程序進行內(nèi)存空間優(yōu)化處理之前,必須對被優(yōu)化的目標(biāo)進行有效分析。開發(fā)人員只有通過內(nèi)存測試過程發(fā)現(xiàn)程序中哪部分代碼需要進行優(yōu)化,才能夠針對實際情況選擇相應(yīng)的優(yōu)化策略。有多種方式可以用于發(fā)現(xiàn)Java程序中的內(nèi)存泄漏現(xiàn)象。最簡單的方法就是使用一個操作系統(tǒng)進程監(jiān)視器,它可以提供一個正在運行的進程所使用的內(nèi)存數(shù)t,也可以使用JavaRuntime類中提供的totalMemory()和freeMemory()等方法來得到虛擬機所控制的連續(xù)內(nèi)存空間的內(nèi)存容量t,以及在特定時刻未使用的內(nèi)存容量t,通過將兩個方法捆綁在一起使用,可以計算出當(dāng)前運行的Java程序所使用的內(nèi)存量。大多數(shù)商業(yè)用的Java集成開發(fā)環(huán)境并沒有提供虛擬機級的控制,因此通常可以通過JDK來完成對內(nèi)存使用狀況的測試。
在Java虛擬機中優(yōu)化Java程序設(shè)計,就是充分利用軟硬件資源,根據(jù)Java對象分配規(guī)則,采取相應(yīng)的
Layout Rules of Memory Space Based on Java Program
Yang Jing,Yang Guanci
(Key Laboratory of Ministry of Education for Advanced Manufacturing Technology,Guizhou University,Guiyang 550000,China)
Frequent interaction between Java objects will cause high memory occupancy and low processing efficiency when the Java virtual machine in the treatment of the large Java program in memory space of a certain size.A new method to reduce the memory space occupancy is given.The method uses the object allocation rules,comprehensive the object properties and influence of memory size and other indicators,obtains the optimal method between the different attributes of the same object by the rules of order.The experiment results show that the method can greatly save memory space and effectively improve the operating efficiency of the Java virtual machine.The more program objects,the effect is more obvious.
Java program;memory space;layout rules;property;object
貴州省優(yōu)秀青年科技人才培養(yǎng)對象專項資金項目(黔科合人字(2015)13號)。
TP311.1
A