本文目錄一覽:
java中怎麼算一個對象的空間大小
Object流
直接將Object寫入或讀出
也叫做對象的序列化:把一個Object直接轉換成位元組流寫到硬碟上,或者直接寫到網路上去
這裡有一個類T:
class T implements Serializable { // 注意,必須要實現Serializable介面,才是可序列化的
int i = 10;
int j = 9;
double d = 2.3;
int k = 15;
}
這個類裡面的成員變數3個int,1個double,int是4位元組,double是8位元組
所以對象的大小絕對不會小於20位元組,但是T肯定是從Object類繼承,那麼繼承下來這些內容也
占空間,比如方法什麼的,所以肯定要大於20位元組了
運行下面這個類:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Test {
public static void main(String[] args) {
T t = new T();
try {
FileOutputStream fos = new FileOutputStream(“d:/abc.txt”);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
直接將T對象通過Object流寫到了D盤的名為abc.txt的文件裡面
一個空文本文件是0位元組,找到這個文件以後查看它的屬性,我這裡是67 位元組,
那麼這個對象就是67 位元組
現在在T類中添加一些其他內容,那麼再次輸出,位元組數肯定就比原來大了,它就是把整個對象轉換成
位元組,寫入了文件,這個應該算是比較好的方式了,但是我們無法特別精確的知道對象中哪些內容各自
佔用多少空間,因為那是Java虛擬機的事了,我學的沒那麼深入,水平有限
當然,把一個對象寫進去,也可以讀出來:
FileInputStream fis = new FileInputStream(“”);
ObjectInputStream ois = new ObjectInputStream(fis);
// 當成Object讀出來的,強轉成T類型
T tReaded = (T)ois.readObject();
System.out.println(tReaded.i + ” ” + tReaded.j + ” ” + tReaded.d + ” ” + tReaded.k);
發現列印出的成員變數的值,還是存進去這個對象的值
怎麼確定Java對象的大小
普通對象的結構如下,按64位機器的長度計算
1. 對象頭(_mark), 8個位元組
2. Oop指針,如果是32G內存以下的,默認開啟對象指針壓縮,4個位元組
3. 數據區
4.Padding(內存對齊),按照8的倍數對齊
數組對象結構是
1. 對象頭(_mark), 8個位元組
2. Oop指針,如果是32G內存以下的,默認開啟對象指針壓縮,4個位元組
3. 數組長度,4個位元組
4. 數據區
5. Padding(內存對齊),按照8的倍數對齊
清楚了對象在內存的基本布局後,咱們說兩種計算Java對象大小的方法
1. 通過java.lang.instrument.Instrumentation的getObjectSize(obj)直接獲取對象的大小
2. 通過sun.misc.Unsafe對象的objectFieldOffset(field)等方法結合反射來計算對象的大小
java.lang.instrument.Instrumentation.getObjectSize()的方式
先講講java.lang.instrument.Instrumentation.getObjectSize()的方式,這種方法得到的是Shallow Size,即遇到引用時,只計算引用的長度,不計算所引用的對象的實際大小。如果要計算所引用對象的實際大小,可以通過遞歸的方式去計算。
java.lang.instrument.Instrumentation的實例必須通過指定javaagent的方式才能獲得,具體的步驟如下:
1. 定義一個類,提供一個premain方法: public static void premain(String agentArgs, Instrumentation instP)
2. 創建META-INF/MANIFEST.MF文件,內容是指定PreMain的類是哪個: Premain-Class: sizeof.ObjectShallowSize
3. 把這個類打成jar,然後用java -javaagent XXXX.jar XXX.main的方式執行
有興趣可以看下博主的:
如何計算Java對象所佔內存的大小
java中可以用.getBytes().length獲取字元串佔用內容的大小,原理是java中任何字元都採用Unicode編碼,所以衡量佔用內存大小採用佔用的位元組數。
舉例如下:
public class TestStringSize {
public static final void main(String[] args) {
System.out.println(“佔用內存大小:”+”學java”.getBytes().length);
}
}
輸出結果:
佔用內存大小:6 byte
如何計算java對象的大小
首先,我們先寫一段大家可能不怎麼寫或者認為不可能的代碼:一個類中,幾個類型都是private類型,沒有public方法,如何對這些屬性進行讀寫操作,看似不可能哦,為什麼,這違背了面向對象的封裝,其實在必要的時候,留一道後門可以使得語言的生產力更加強大,對象的序列化不會因為沒有public方法就無法保存成功吧,OK,我們簡單寫段代碼開個頭,逐步引入到怎麼樣去測試對象的大小,一下代碼非常簡單,相信不用我解釋什麼:
import java.lang.reflect.Field;
class NodeTest1 {
private int a = 13;
private int b = 21;
}
public class Test001 {
public static void main(String []args) {
NodeTest1 node = new NodeTest1();
Field []fields = NodeTest1.class.getDeclaredFields();
for(Field field : fields) {
field.setAccessible(true);
try {
int i = field.getInt(node);
field.setInt(node, i * 2);
System.out.println(field.getInt(node));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
代碼最基本的意思就是:實例化一個NodeTest1這個類的實例,然後取出兩個屬性,分別乘以2,然後再輸出,相信大家會認為這怎麼可能,NodeTest1根本沒有public方法,代碼就在這裡,將代碼拷貝回去運行下就OK了,OK,現在不說這些了,運行結果為:
26
42
為什麼可以取到,是每個屬性都留了一道門,主要是為了自己或者外部接入的方便,相信看代碼自己仔細的朋友,應該知道門就在:field.setAccessible(true);代表這個域的訪問被打開,好比是一道後門打開了,呵呵,上面的方法如果不設置這個,就直接報錯。
看似和對象大小沒啥關係,不過這只是拋磚引玉,因為我們首先要拿到對象的屬性,才能知道對象的大小,對象如果沒有提供public方法我們也要知道它有哪些屬性,所以我們後面多半會用到這段類似的代碼哦!
對象測量大小的方法關鍵為java提供的(1.5過後才有):java.lang.instrument.Instrumentation,它提供了豐富的對結構的等各方面的跟蹤和對象大小的測量的API(本文只闡述對象大小的測量方法),於是乎我心喜了,不過比較噁心的是它是實例化類:sun.instrument.IntrumentationImpl是sun開頭的,這個鬼東西有點不好搞,翻開源碼構造方法是private類型,沒有任何getInstance的方法,寫這個類幹嘛?看來這個只能被JVM自己給初始化了,那麼怎麼將它自己初始化的東西取出來用呢,唯一能想到的就是agent代理,那麼我們先拋開代理,首先來寫一個簡單的對象測量方法:
步驟1:(先創建一個用於測試對象大小的處理類)
import java.lang.instrument.Instrumentation;
public class MySizeOf {
private static Instrumentation inst;
/**
*這個方法必須寫,在agent調用時會被啟用
*/
public static void premain(String agentArgs, Instrumentation instP) {
inst = instP;
}
/**
* 直接計算當前對象佔用空間大小,包括:當前類及超類的基本類型實例欄位大小
* 引用類型實例欄位引用大小、實例基本類型數組總佔用空間、實例引用類型數組引用本身佔用空間大小
* 但是不包括超類繼承下來的和當前類聲明的實例引用欄位的對象本身的大小、實例引用數組引用的對象本身的大小
* 用來測量java對象的大小(這裡先理解這個大小是正確的,後面再深化)
*/
public static long sizeOf(Object o) {
if(inst == null) {
throw new IllegalStateException(“Can not access instrumentation environment.\n” +
“Please check if jar file containing SizeOfAgent class is \n” +
“specified in the java’s \”-javaagent\” command line argument.”);
}
return inst.getObjectSize(o);
}
}
步驟2:上面我們寫好了agent的代碼,此時我們要將上面這個類編譯後打包為一個jar文件,並且在其包內部的META-INF/MANIFEST.MF文件中增加一行:Premain-Class: MySizeOf代表執行代理的全名,這裡的類名稱是沒有package的,如果你有package,那麼就寫全名,我們這裡假設打包完的jar包名稱為agent.jar(打包過程這裡簡單闡述,就不細說了),OK,繼續向下走:
步驟3:編寫測試類,測試類中寫:
public class TestSize {
public static void main(String []args) {
System.out.println(MySizeOf.sizeOf(new Integer(1)));
System.out.println(MySizeOf.sizeOf(new String(“a”)));
System.out.println(MySizeOf.sizeOf(new char[1]));
}
}
如何計算java中的對象object大小size
// 利用GC回收前與回收後的差值計算對象的大小:
class Foo{ // 32位OS類定義引用佔8 byte,64位OS佔用16 byte
int x; // 4 byte
byte b; // 1 byte
}
public class Demo {
public static void main(String args[]) {
Foo foo= new Foo();
Runtime.getRuntime().gc();
long gcing = Runtime.getRuntime().freeMemory();
Foo foo2= new Foo();
long gced = Runtime.getRuntime().freeMemory();
// 64位列印24,32位列印16 (注:是因為JVM底層內存都是以8 byte對齊的,即8的倍數)
System.out.println(“Memory used:”+(gcing -gced ));
}
}
原創文章,作者:簡單一點,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/130276.html