本文目录一览:
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 ));
}
}