研究一下JAVA的SizeOf
引用外部類實現JAVA的SizeOf
JAVA本身是沒有SizeOf的,因此我們需要去MavenRepository中下載JAR包(也可以使用maven等),因為這裡只是做一個簡單測試,就直接下載了JAR包。
點選jar下載,最新的版本也是2015年,算是比較老了。
下載成功後匯入自己的JAVA專案,具體怎麼匯入網上有很多教程,就不贅述了。
可以看到我這裡已經引用成功了,import後可以直接使用這些類了。
簡單的測試
接下來來做一些簡單的測試,看看這個類提供的sizeOf方法是否準確。
System.out.println(RamUsageEstimator.sizeOf(new Object()));
在控制檯我們可以看到輸出的結果是16。
可能還有人不清楚為什麼會是16,這邊給出一個連結,可以學習一下,個人覺得還是挺有幫助的。
Integer等也都可以測試一下,結果還是很正確的。
那比較重要的就是陣列和物件中再包含物件的,能否還是比較精確呢?
我們先來寫一個簡單的物件。
先給出一些例子,大家想想是為什麼。
import com.carrotsearch.sizeof.RamUsageEstimator;
public class SizeOfTest {
public static void main(String[] args) {
System.out.println(RamUsageEstimator.sizeOf(new MemTest()));
}
}
class MemTest{
Integer a = 12;
}
結果是32,其實挺好解釋的,MemTest本身和Object物件一樣,佔用12位元組,a這個引用在指標壓縮後佔用4位元組,加上a的16,自然是32。
我們把Integer改成int,結果答案還是16。
再加上一個int b,就變成了24。這是因為指標對齊。
如果你剛才有仔細去研究那個連結的話,你會看到Object本來應該佔12個位元組的,只是為了對齊變成了16。加上一個int的4位元組,剛好是16。但我們改成Integer a;發現結果也是16。難道a不應該是8個位元組的地址的引用嗎?可以去了解一下指標壓縮,預設是開啟的,因此佔用四個位元組。
這樣就可以解釋的通了,再多做幾次測試,加上各種各樣的物件,也是這樣的,符合我們計算出來的值。
再測試一種比較有意思的情況:
這種情況顯示的是40。
這種情況卻是56。
這是因為Java在處理Integer時使用了一個快取,其中快取了-128到127之間的數字對應的Integer物件。
看來原始碼應該不是簡單的相加。(還沒來得及研究原始碼)
對研究JAVA記憶體結構很有幫助。
研究陣列的情況
接下來來研究一下陣列的測試是否準確,陣列佔用空間之前應用RunTime下提供的jvm記憶體判斷的工具測試過,但那種方法不太準確,只能通過加大陣列中元素個數來測試一個JAVA物件佔用空間(所以Object一個物件佔16位元組我是測試過的!感興趣的人可以看這個網址https://blog.csdn.net/ithomer/article/details/7310008)。同時還得出了結論,int[2][1024*1024]佔用的記憶體比int[1024*1024][2]要小得多,這個也很好理解,JAVA的二維陣列其實就是以陣列為元素的陣列,陣列中自然有描述的一些東西,陣列越多自然佔用空間越大。
其實一維陣列的情況不難,一維陣列就相當於一個物件頭+一片陣列資料空間。
這裡要注意:陣列的物件頭是這樣的,MarkWord佔用8位元組,Class Point佔用4位元組,Length 陣列佔用4位元組。
比如int[1]就是8+4+4+4,再對齊,就是24,測試發現相符。
那二維陣列呢,我們先理論分析一下,以int[2][2]為例,先從二維的角度來看,物件頭應該是16個位元組,兩個一維的指標一共8位元組,兩個一維陣列各自如上佔用24位元組。也就是16+8+48=72位元組,驗證一下,果不其然。
總結
對這個SizeOf的測試就到此為止啦,主要是精確度的測試,結合網上查到的資料,精確度應該還行。
JAVA的記憶體實在太複雜了,方法區,棧記憶體,特別是常量池又分為了好幾種。。。瞭解的不夠透徹,陷入了好幾次圈圈中,查了好多資料才瞭解,因此決定也發個部落格回饋一下。
之後準備測試一下比較複雜的情況下的精確度和效率。