都知道堆內(nèi)存要回收垃圾,如何在開發(fā)中使用對象來減少內(nèi)存使用
本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)公眾號。
堆內(nèi)存
我們大家都知道JVM內(nèi)存是劃分了一個是堆內(nèi)存,一個是非堆內(nèi)存,而堆內(nèi)存分為了(年輕代),(老年代)這些,而非堆內(nèi)存就是一個元空間了(1.8之后變更的,之前是永久代)。
相比較來說,大家肯定都非常的熟悉分代的概念,知道對象首先應(yīng)該放在哪里,然后移動到哪里,最后執(zhí)行什么樣子的方法來進行垃圾回收。而我們今天要說的卻是如何考慮運行程序的機器內(nèi)存限制下,讓我們的對象更小一點就能完成我們的功能。
減少對象大小
阿粉和大家都一樣,都知道對象會占用一定數(shù)量的堆內(nèi)存,畢竟你新生成的對象首先就是要放到Eden區(qū)的,當Eden空間被占滿的時候,出發(fā)Minor GC,存活下來的對象移動到Survivor區(qū)去,而我們想要減少內(nèi)存的使用,最簡單的方法就是在寫程序的時候,也需要考慮對象的大小,畢竟如果說如果說以后再做CodeReview的時候,你會發(fā)現(xiàn)你的代碼運行起來,你看JVM的時候會賞心悅目,但是代碼也得好看不是?
阿粉就給大家看看最基礎(chǔ)的Java基礎(chǔ)實例變量的大小
圖1:
實例變量,這是一個和對象息息相關(guān)的,一個對象一份實例變量,而實例變量的個數(shù)和實例變量的大小也就決定你在占用內(nèi)存的大小。
大家可以想象一下,如果內(nèi)存不夠,那么有兩種方式供你選擇:
- 選擇一:增加百分之10的堆內(nèi)存
- 選擇二:堆中的對象的大小減少百分之10
你會選擇什么方式?一般情況你想選擇第一種方式,但是這種方式好像不是那么的實際,你堆內(nèi)存都不夠了,你還想再繼續(xù)增加點?那么只能你來選擇第二種了。
但是再你選擇了第二種方式之后,你又遇到了一個問題,減少對象的方式也是有兩種方式:
- 方式一:直接減少實例變量的數(shù)量
- 方式二:減少實例變量的大小
其實這兩種方式都可以,這個就是要取決于你在之前代碼中做過什么,比如說你在之前的代碼已經(jīng)進行過實例變量的優(yōu)化了,在寫代碼之前就已經(jīng)考慮到這件事了,那么你肯定是只能選擇第一種。
如果說你之前在代碼中并沒有去考慮過實例變量的大小,那么選擇第一種將會是你最佳的方案。
分析對象大小
一個對象的大小,我們要把它分開,由三部分來組成,對象頭、實例變量、內(nèi)存補充,在32位的系統(tǒng)中,假設(shè)我們定義一個int i ,那么對象頭在其中就要占據(jù) 4 字節(jié),int 在對象中占用 4 字節(jié),而如果是64位的話,那么對象頭就變了,從4字節(jié)變成8字節(jié),在這里我們就得注意一個事情了,如果說成員變量不論是否引用了其他的對象,它占用的字節(jié)始終是 4 字節(jié)。
這里我們就引入了一個概念:Shallow Size
Shallow Size
其實簡單來說,Shallow Size 就是對象本身占用內(nèi)存的大小,但是不包含其中引用的對象,這局話的后半段就是相對應(yīng)的阿粉剛才所說的注意事項了。
而 Shallow Size 也是有針對的,就比如說是非數(shù)組類型的對象,他的大小就是對象和他所有成員變量大小的總和,
針對數(shù)組類型的對象,它的大小是數(shù)組元素對象的大小的總和。
舉個例子:
- public class A(){
- private int i ;
- private boolen x;
- }
我們的A對象在我們New出來之后,發(fā)現(xiàn),不是一個數(shù)組類型的,那么就得看成員變量,然后把成員變量加起來,是不是就等于 Shallow Size 了。
Retained Size
說了Shallow Size了,那么我們就不得不提 Retained Size了,因為阿粉在學(xué)習(xí)的時候,去專門翻找了資料,發(fā)現(xiàn)這都是一體的,你看這個,你發(fā)現(xiàn)下面還有和他有關(guān)聯(lián)的,不學(xué)吧,弄不明白心里難受,那還是學(xué)習(xí)吧。
英文復(fù)制
Retained Size = 當前對象的大小+當前對象的引用大小(直接或者間接)都是
示例圖:
圖片網(wǎng)址如下https://www.yourkit.com/docs/java/help/sizes.jsp 里面也有解釋,但是阿粉還是要解釋一波。
在上圖中 obj1 的 Retained Size = obj1 + obj2 + obj4 的 Shallow size
這是左邊的,右邊的是obj1 的 Retained Size = obj1 + obj2 + obj4 +obj3 的 Shallow size
在我們進行GC的時候,Retained Size是必不可少的,它有助于了解內(nèi)存的結(jié)構(gòu)(聚類)和對象子圖之間的依賴關(guān)系,以及查找這些子圖的潛在根源。
不過說實在的,因為JVM的存在,他自己的垃圾回收機制已經(jīng)算是非常的不錯了,但是因為我們在日常的業(yè)務(wù)中的需要,我們?nèi)匀恍枰W(xué)習(xí)這些內(nèi)容,畢竟萬一在以后的實際工作中真的遇到了,你會發(fā)現(xiàn)你現(xiàn)在學(xué)的內(nèi)容是非常有用的。
文獻參考
《YouKit》 《Java性能權(quán)威指南》