`
hcwj2009
  • 浏览: 26745 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java 对象(转载)

 
阅读更多
在大规模Java 应用开发中,总会遇到内存泄漏的问题。通常的做法,通过 Profile 工具,分析 Java  Heap ,一般能够发现哪些对象内存占用巨大,而引起的泄漏问题。为了更好地深入了解问题的本质,以及从另外一个角度来分析问题,特写这篇文章。

原文地址:
可能不少的读者,并不清楚Java 对象到底占居多少的空间(单位:字节 =8 比特)。文章中会使用 JDK 6 update 7 自带的 Profile 工具 -Java VisualVM 。引入 Profile 工具的目的正是为了分析对象的大小。

首先,要区别Java 对象和 Java 类元信息,其中, JVM 把所有的 Java 对象放到 Java Heap 中,而类的元信息是放在方法区的,通俗地说,在 Java 源代码中,定义的字段和方法等变量标示(比如字段名称和类型等)。请注意,元信息所引用的对象还是在 Java Heap 里面。那么,本章主要 针对 的是Java Heap 。

       早几天,看到了JavaEye 上面提问 -http://www.iteye.com/problems/45423 。问题的本质,和主题一样,不过它想要通过 Java 程序来计算,貌似有点困难。以前外国一个哥们( http://www.javaworld.com/javaworld/javatips/jw-javatip130.html )也写 个 一个程序计算对象大小,它的计算如下:
public class Sizeof
{
    public static void main (String [] args) throws Exception
    {
        // Warm up all classes/methods we will use
        runGC ();
        usedMemory ();
        // Array to keep strong references to allocated objects
        final int count = 100000;
        Object [] objects = new Object [count];       
        long heap1 = 0;
        // Allocate count+1 objects, discard the first one
        for (int i = -1; i < count; ++ i)
        {
            Object object = null;
            // Instantiate your data here and assign it to object
             object = new Object ();
            if (i >= 0)
                objects [i] = object;
            else
            {
                object = null; // Discard the warm up object
                runGC ();
                heap1 = usedMemory (); // Take a before heap snapshot
            }
        }
        runGC ();
        long heap2 = usedMemory (); // Take an after heap snapshot:       
        final int size = Math.round (((float)(heap2 - heap1))/count);
        System.out.println ("'before' heap: " + heap1 +
                            ", 'after' heap: " + heap2);
        System.out.println ("heap delta: " + (heap2 - heap1) +
            ", {" + objects [0].getClass () + "} size = " + size + " bytes");
        for (int i = 0; i < count; ++ i) objects [i] = null;
        objects = null;
    }
    private static void runGC () throws Exception
    {
        // It helps to call Runtime.gc()
        // using several method calls:
        for (int r = 0; r < 4; ++ r) _runGC ();
    }
    private static void _runGC () throws Exception
    {
        long usedMem1 = usedMemory (), usedMem2 = Long.MAX_VALUE;
        for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++ i)
        {
            s_runtime.runFinalization ();
            s_runtime.gc ();
            Thread.currentThread ().yield ();
           
            usedMem2 = usedMem1;
            usedMem1 = usedMemory ();
        }
    }
    private static long usedMemory ()
    {
        return s_runtime.totalMemory () - s_runtime.freeMemory ();
    }
   
    private static final Runtime s_runtime = Runtime.getRuntime ();
} // End of class
( 代码 1)

通过改变红色区域,来切换测试对象。先运行出结果,以下结果是在Windows XP x86  ,SUN JDK 1.6.0 update 7 ,并且Console 信息部分被截断:

{class java.lang.Object} size = 8 bytes
{class java.lang.Integer} size = 16 bytes
{class java.lang.Long} size = 16 bytes
{class java.lang.Byte} size = 16 bytes
{class [Ljava.lang.Object;} size = 16 bytes //长度为0的Object类型数组
{class [Ljava.lang.Object;} size = 16 bytes //长度为1的Object类型数组
{class [Ljava.lang.Object;} size = 24 bytes //长度为2的Object类型数组

现在,这个结论有问题,因为,从Byte、Long、Integer源代码的角度,不可能对象空间大小相同,那么接下来需要借助于Java VisualVM来做了。 在测试之前 ,需要一段辅助程序,帮助执行。实现如下:

public   class  MainClass {
/**
* 启动方法
*  @param  args
*/
public   static   void  main(String[] args)  throws  Exception {
Object object =  new  Object();
neverStop ();
neverGC (object);
}
private   static   void  neverGC(Object object) {
System. out .println(object);
}
private   static   void  neverStop()  throws  InterruptedException {
Thread. sleep (Long. MAX_VALUE );
}
}
( 代码2 )
这个程序保证对象不会被GC掉,并且不会停止。
首先,在Java VisualVM上面,选择正确的程序进行监控,然后做一个Heap Dump。


(图1)

Dump之后,点击 Classes ,然后过滤出 java.lang.Object ,如图所示:


(图2)


(图3)


图 3中 过滤出三个结果,暂时不看数组对象,选择java.lang.Object ,然后点击 instances 按钮,或者双击 java.lang.Object 。



( 图 4)
结果发现,Object 对象的大小总是 8 个字节。再看看 Integer 、 Long 和 Byte 。 Sizeof 类的计算结果不可信,分析错误原因放一下,后面会提到。先来分析 Integer 、 Long 和 Byte 。从源代码的角度来分析, Integer 包含了一个 int 的 value, 其他的也有对应类型。而知晓, int 占用 4 个字节, long 则是 8 个字节, byte 占用 1 个字节。看图说话:




(图5)


(图6)


(图7)


Integer对象空间大小是 12 字节(见图 5) , Long 对象空间则是 16 个字节(见图 6 ), Byte 空间则是 9 个字节(见图 7 )。那么除去对象自身的状态 value ,得出的结论是,最后“空壳”对象都 8 字节?不能确定,因为还有类(静态)字段没有考虑,这些字段是否属于对象实例的一部分呢?很多书上面再三强调类的字段不属于对象实例,对于这种说法需要保持怀疑的态度?下面做一个“空壳”对象实验,代码如下:

/**
* 和Object一样,没有任何添加对象状态。
*  @author  mercy
*/
public   class  SameAsObject  extends  Object {
}
( 代码3 )
修改 MainClass程序, new  一个 SameAsObject 对象,重新 Heap Dump,结果图:





(图8)

图8 中表明,证明了“空壳”对象空间大小 8 字节,和 java.lang.Object 类似。目前还不能证明 Integer 的情况,因为 Integer 类的层次是 Integer <- Number <- Object 。虽然 Number 没有对象状态,可是也不能证明出去 value 的 Integer 对象空间大小等于 Object 的。为了证明这一点,再扩展一下 SameAsObject, 使其成为父类,创建一个子类 ExtSameAsObject 。同样的方法,获取大小:



(图9)

图9 中, ExtSameAsObject 的空间大小还是 8 字。 证明了空壳对象大小等同于java.lang.Object 的。 那么自然地可以推导出java.lang.Double 也是 16 字节( 8 字节空壳对象 +8 字节的 double 类型 value) 。

细心的读者会发现,图8 和 9 中,笔者标记了红色区域,都有 Java frame 的标识。而在MainClass 的 main 方法中,有一句: Object object =  new  ExtSameAsObject(); 这个对象是局部变量。说明什么问题呢? 两个问题:第一,正因为在方法内部执行,这个语句就是一个 frame,而 frame 是 Java Stack 的组成单位。这个 frame 被压入栈,并且类型是 ExtSameAsObject ,同时带有一个对象的地址 #1 (当然这里不是实际地址,只是一个引用标识)。第二,即使是局部对象,对象仍然分配在 Java Heap 中。

既然“空壳”的Integer 对象,占用 8 个字节的大小,那么类的成员就不应该归入对象实例之中。从实验中,我们可以得出结论, 某个类的任何类成员的常量和变量,都不会分配(或计算)到该类的对象实例。

回到Sizeof 类,为什么 Sizeof 会计算出问题?虽然 Sizeof 类计算有误,不过它的思想还是几点值得借鉴:

第一、 作者深入了了解了Runtime#gc() 方法和 Runtime#runFinalization 方法的语义。这两个方法并不是实时执行,而是建议 JVM 执行,执行与否程序怎么知道呢 ? 在— _runGC 方法中,作者试图通过内存的变化来判断是否 GC ( GC 后,肯定会变化的),确实有道理。

第二、 通过N 次,求得平均数,比单次测试要精确很多。

第三、 没有开辟其他对象,不影响结果。

同时,不过作者忽略了如下情况:

第一、 GC不只是针对需要测试的对象,而是整个 JVM 。因此在 GC 的时候,有可能是 JVM 启动后,把其他没有使用的对象给 GC 了,这样造成了使用空间的变大,而 Heap 空间变小。

第二、 Runtime#freeMemory()方法,返回的数据是 近视值 ,不一定能够保证正确性。因此在后面的累计、求平均数来带了误差。

第三、 使用了Math#round() 方法,又带来了误差。

由于Sizeof 的不精确,不能作为测试基准。

那么,更多的疑问产生了。前面测试的都是单一对象,那么数组对象是如何分配的?

修改程序如下:

public   static   void  main(String[] args)  throws  Exception {
//Object object = new ExtSameAsObject();
Object [] object =  new   Object [0];
...
( 代码4 )

重新Heap Dump:


(图10)

余下内容,看附件。错误内容已经修正,请下载Fix1文档 。

Dive_in_Java_Object_size_fix_1_.zip (270.8 KB)
下载次数: 407
查看图片附件


分享到:   
何必较真 | Java面向对象设计最佳实践 - 方法设计(二 ...
19:10评论 / 浏览 (32 / 1393)论坛回复 / 浏览 (29 / 7638)收藏分类:企业架构相关推荐
评论
32 楼 hoszb 2010-08-28   引用
profile工具,我喜欢JRockit的jrmc,感觉功能比原来的Sun那个jvisualvm好用
31 楼 mercyblitz 2010-08-28   引用
hoszb 写道
mercyblitz 写道


写得不错,让我萌生了一个想法-递归计算空间大小。


发现这个代码还需要做大改动,有时候多个对象共享一个成员变量,还有时,一个对象循环引用自己:

public class A{
    A a = this;
}


更糟糕的是,刚发现的

不同的JVM,内存管理不一样, 导致一个引用的大小有可能不同,相同长度的数组占用的空间也有可能不同。

(1).32位和64位的JVM有不同

(2).不同厂商的相同位数的JVM也有不同

(3).相同厂商不同版本的JVM,如1.5,1.6的。。。也有可能不同

引用大小:SUN的32位JVM,4字节,SUM的64位JVM,8字节,JRockit的32位/64位JVM,引用都是4字节


另外JVM在server模式下的System.gc()方法难以生效,测试时要在client模式下测



以上这些还是已知的,还有未知的不同。。。。。


嗯,因此Profile工具是必要的,程序写的东西,不确定因素很多。
30 楼 hoszb 2010-08-28   引用
mercyblitz 写道


写得不错,让我萌生了一个想法-递归计算空间大小。


发现这个代码还需要做大改动,有时候多个对象共享一个成员变量,还有时,一个对象循环引用自己:

public class A{
    A a = this;
}


更糟糕的是,刚发现的

不同的JVM,内存管理不一样, 导致一个引用的大小有可能不同,相同长度的数组占用的空间也有可能不同。

(1).32位和64位的JVM有不同

(2).不同厂商的相同位数的JVM也有不同

(3).相同厂商不同版本的JVM,如1.5,1.6的。。。也有可能不同

引用大小:SUN的32位JVM,4字节,SUM的64位JVM,8字节,JRockit的32位/64位JVM,引用都是4字节


另外JVM在server模式下的System.gc()方法难以生效,测试时要在client模式下测



以上这些还是已知的,还有未知的不同。。。。。
29 楼 mercyblitz 2010-08-28   引用
kuanchang 写道
这么深入的帖子,竟然是新手帖???那些人给的?

这个我认了,大家都是高手!

民主就是这么来的!
28 楼 kuanchang 2010-08-28   引用
这么深入的帖子,竟然是新手帖???那些人给的?
27 楼 mercyblitz 2010-08-28   引用
hoszb 写道
我研究了一晚上,写了一个精确计算对象占用空间的代码,楼主有空帮我测试一吧下:

这个代码在符合我的电脑(64位CPU,装32位操作系统,32位sun的虚拟机)的内存使用情况。
我电脑内存使用情况是拿gc()后的内存增长量做测试的。

Java代码 
import java.lang.reflect.*; 
 
//一个引用:4字节 
//一个Object:8字节 
//一个Integer:16字节 == (8 + 4) / 8 * 8 
//一个int:4字节 
//长度l的byte数组:(l+19)/8*8 
//长度l的char/short数组:(l*2+19)/8*8 == (l+9)/4*8 
//长度l的String:(l+1)/4*8+40 
//长度l的int数组:(l*4+19)/8*8 ==(l+4)/2*8 
//长度l的long数组:(l*8+19)/8*8 == (l+2)*8 
public class Occupy { 
    //不写不行... 
    public static int occupyof(boolean variable) {return 1;} 
    public static int occupyof(byte variable) {return 1;} 
    public static int occupyof(short variable) {return 2;} 
    public static int occupyof(char variable) {return 2;} 
    public static int occupyof(int variable) {return 4;} 
    public static int occupyof(float variable) {return 4;} 
    public static int occupyof(long variable) {return 8;} 
    public static int occupyof(double variable) {return 8;} 
     
    public static int occupyof(Object object) { 
        if (object == null) 
            return 0; 
        int size = 8; 
        Class clazz = object.getClass(); 
        if (clazz.isArray()) { 
            size = 4;//length变量是int型 
            Class<?> componentType = clazz.getComponentType(); 
            if (componentType.isPrimitive()) 
                return occupyofSize(size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType)); 
            Object[] array = (Object[]) object; 
            size += 4 * array.length; 
            for (Object o : array) 
                size += occupyof(o); 
            return occupyofSize(size); 
        } 
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) { 
            if (Modifier.isStatic(field.getModifiers())) 
                continue;//类成员不计 
            Class<?> type = field.getType(); 
            if (type.isPrimitive()) 
                size += sizeofPrimitiveClass(type); 
            else { 
                size += 4;//一个引用型变量占用4个字节 
                try { 
                    field.setAccessible(true);//可以访问非public类型的变量 
                    size += occupyof(field.get(object)); 
                } catch (Exception e) { 
                    size += occupyofConstructor(object, field); 
                } 
            } 
        } 
        return occupyofSize(size); 
    } 
 
    public static int sizeof(boolean variable) {return 1;} 
    public static int sizeof(byte variable) {return 1;} 
    public static int sizeof(short variable) {return 2;} 
    public static int sizeof(char variable) {return 2;} 
    public static int sizeof(int variable) {return 4;} 
    public static int sizeof(float variable) {return 4;} 
    public static int sizeof(long variable) {return 8;} 
    public static int sizeof(double variable) {return 8;} 
     
    public static int sizeof(Object object) { 
        if (object == null) 
            return 0; 
        int size = 8; 
        Class clazz = object.getClass(); 
        if (clazz.isArray()) { 
            size = 4;//length变量是int型 
            Class<?> componentType = clazz.getComponentType(); 
            if (componentType.isPrimitive()) 
                return size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType); 
            Object[] array = (Object[]) object; 
            size += 4 * array.length; 
            for (Object o : array) 
                size += sizeof(o); 
            return size; 
        } 
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) { 
            if (Modifier.isStatic(field.getModifiers())) 
                continue;//类成员不计 
            Class<?> type = field.getType(); 
            if (type.isPrimitive()) 
                size += sizeofPrimitiveClass(type); 
            else { 
                size += 4;//一个引用型变量占用4个字节 
                try { 
                    field.setAccessible(true);//可以访问非public类型的变量 
                    size += sizeof(field.get(object)); 
                } catch (Exception e) { 
                    size += sizeofConstructor(object, field); 
                } 
            } 
        } 
        return size; 
    } 
     
    private static int occupyofConstructor(Object object, Field field) { 
        throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field); 
    } 
 
    private static int sizeofConstructor(Object object, Field field) { 
        throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field); 
    } 
 
    /**
     * 对象的大小 和 占用空间并不相等,就好象Windows下文件一样(大小为1字节时占用空间4k)
     * 对象占用空间的增长以8个字节为单位,占用空间=大小对8的无条件进位法,
     * 即occupy = (size + 8 - 1) / 8 * 8;   例如:
     * 大小8字节:占用8字节,(new Object()就是占用8字节)
     * 大小9字节:占用16字节
     * 大小16字节:占用16字节
     * 大小17字节:占用24字节
     * @param size 大小,以字节为单位
     * @return 占用空间
     */ 
    private static int occupyofSize(int size) { 
        return (size + 7) / 8 * 8; 
    } 
 
    private static int sizeofPrimitiveClass(Class clazz) { 
        return clazz == boolean.class || clazz == byte.class ? 1 : clazz == char.class || clazz == short.class ? 2 : clazz == int.class || clazz == float.class ? 4 
                : 8; 
    } 
 
    private static int lengthOfPrimitiveArray(Object object) { 
        Class<?> clazz = object.getClass(); 
        return clazz == boolean[].class ? ((boolean[]) object).length 
        : clazz == byte[].class ? ((byte[]) object).length 
        : clazz == char[].class ?   ((char[]) object).length 
        : clazz == short[].class ? ((short[]) object).length 
        : clazz == int[].class ?  ((int[]) object).length 
        : clazz == float[].class ? ((float[]) object).length 
        : clazz == long[].class ?    ((long[]) object).length 
        : ((double[]) object).length; 
    } 
     
    public static void main(String[] args) throws Throwable { 
        System.out.println(occupyof(new String("Web.Zhu"))); 
    } 
 



写得不错,让我萌生了一个想法-递归计算空间大小。
26 楼 hoszb 2010-08-28   引用
我研究了一晚上,写了一个精确计算对象占用空间的代码,楼主有空帮我测试一吧下:

这个代码在符合我的电脑(64位CPU,装32位操作系统,32位sun的虚拟机)的内存使用情况。
我电脑内存使用情况是拿gc()后的内存增长量做测试的。

Java代码 
import java.lang.reflect.*; 
 
//一个引用:4字节 
//一个Object:8字节 
//一个Integer:16字节 == (8 + 4 + 7) / 8 * 8 
//一个int:4字节 
//长度l的byte数组:(l+19)/8*8 
//长度l的char/short数组:(l*2+19)/8*8 == (l+9)/4*8 
//长度l的String:(l+1)/4*8+40 
//长度l的int数组:(l*4+19)/8*8 ==(l+4)/2*8 
//长度l的long数组:(l*8+19)/8*8 == (l+2)*8 
public class Occupy { 
    //不写不行... 
    public static int occupyof(boolean variable) {return 1;} 
    public static int occupyof(byte variable) {return 1;} 
    public static int occupyof(short variable) {return 2;} 
    public static int occupyof(char variable) {return 2;} 
    public static int occupyof(int variable) {return 4;} 
    public static int occupyof(float variable) {return 4;} 
    public static int occupyof(long variable) {return 8;} 
    public static int occupyof(double variable) {return 8;} 
     
    public static int occupyof(Object object) { 
        if (object == null) 
            return 0; 
        int size = 8;//对象本身8字节 
        Class clazz = object.getClass(); 
        if (clazz.isArray()) { 
            size += 4;//length变量是int型 
            Class<?> componentType = clazz.getComponentType(); 
            if (componentType.isPrimitive()) 
                return occupyofSize(size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType)); 
            Object[] array = (Object[]) object; 
            size += 4 * array.length; 
            for (Object o : array) 
                size += occupyof(o); 
            return occupyofSize(size); 
        } 
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) { 
            if (Modifier.isStatic(field.getModifiers())) 
                continue;//类成员不计 
            Class<?> type = field.getType(); 
            if (type.isPrimitive()) 
                size += sizeofPrimitiveClass(type); 
            else { 
                size += 4;//一个引用型变量占用4个字节 
                try { 
                    field.setAccessible(true);//可以访问非public类型的变量 
                    size += occupyof(field.get(object)); 
                } catch (Exception e) { 
                    size += occupyofConstructor(object, field); 
                } 
            } 
        } 
        return occupyofSize(size); 
    } 
 
    public static int sizeof(boolean variable) {return 1;} 
    public static int sizeof(byte variable) {return 1;} 
    public static int sizeof(short variable) {return 2;} 
    public static int sizeof(char variable) {return 2;} 
    public static int sizeof(int variable) {return 4;} 
    public static int sizeof(float variable) {return 4;} 
    public static int sizeof(long variable) {return 8;} 
    public static int sizeof(double variable) {return 8;} 
     
    public static int sizeof(Object object) { 
        if (object == null) 
            return 0; 
        int size = 8; 
        Class clazz = object.getClass(); 
        if (clazz.isArray()) { 
            size = 4;//length变量是int型 
            Class<?> componentType = clazz.getComponentType(); 
            if (componentType.isPrimitive()) 
                return size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType); 
            Object[] array = (Object[]) object; 
            size += 4 * array.length; 
            for (Object o : array) 
                size += sizeof(o); 
            return size; 
        } 
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) { 
            if (Modifier.isStatic(field.getModifiers())) 
                continue;//类成员不计 
            Class<?> type = field.getType(); 
            if (type.isPrimitive()) 
                size += sizeofPrimitiveClass(type); 
            else { 
                size += 4;//一个引用型变量占用4个字节 
                try { 
                    field.setAccessible(true);//可以访问非public类型的变量 
                    size += sizeof(field.get(object)); 
                } catch (Exception e) { 
                    size += sizeofConstructor(object, field); 
                } 
            } 
        } 
        return size; 
    } 
     
    private static int occupyofConstructor(Object object, Field field) { 
        throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field); 
    } 
 
    private static int sizeofConstructor(Object object, Field field) { 
        throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field); 
    } 
 
    /**
     * 对象的大小 和 占用空间并不相等,就好象Windows下文件一样(大小为1字节时占用空间4k)
     * 对象占用空间的增长以8个字节为单位,占用空间=大小对8的无条件进位法,
     * 即occupy = (size + 8 - 1) / 8 * 8;   例如:
     * 大小8字节:占用8字节,(new Object()就是占用8字节)
     * 大小9字节:占用16字节
     * 大小16字节:占用16字节
     * 大小17字节:占用24字节
     * @param size 大小,以字节为单位
     * @return 占用空间
     */ 
    private static int occupyofSize(int size) { 
        return (size + 7) / 8 * 8; 
    } 
 
    private static int sizeofPrimitiveClass(Class clazz) { 
        return clazz == boolean.class || clazz == byte.class ? 1 : clazz == char.class || clazz == short.class ? 2 : clazz == int.class || clazz == float.class ? 4 
                : 8; 
    } 
 
    private static int lengthOfPrimitiveArray(Object object) { 
        Class<?> clazz = object.getClass(); 
        return clazz == boolean[].class ? ((boolean[]) object).length 
        : clazz == byte[].class ? ((byte[]) object).length 
        : clazz == char[].class ?   ((char[]) object).length 
        : clazz == short[].class ? ((short[]) object).length 
        : clazz == int[].class ?  ((int[]) object).length 
        : clazz == float[].class ? ((float[]) object).length 
        : clazz == long[].class ?    ((long[]) object).length 
        : ((double[]) object).length; 
    } 
     
    public static void main(String[] args) throws Throwable { 
        System.out.println(occupyof(new String("Web.Zhu"))); 
    } 
 

25 楼 aswang 2010-08-19   引用
楼主钻研的很深入,值得学习!
24 楼 likeblood 2010-07-22   引用
个人以为 由于32为操作系统的原因 必须4字节为一个单元 因为只能是4的倍数 不够的也要补齐 因此 5字节的也是8字节 1字节的是4字节

64位的话 就是8字节一单元了
23 楼 zgz888 2010-07-15   引用
支持一下,有空详细看看
22 楼 kingwood2005 2010-07-15   引用
mark

好文章,写的不错
21 楼 zhao103804 2010-07-14   引用
amazeur 写道
zhao103804 写道
写的不错,我们项目中内存泄露都没有这样精密的计算过,研究对象的大小还没深入到这块,看来要想LZ多多学习了


看来使用的时候要多注意了。


确实,我们项目中解决的办法就是加些限制条件
比如说从数据库中查询几万条数据放到list里面
但是又不知道那个对象到底能装多少,所有只有加限制条件,一次少装点了。
20 楼 kongxx 2010-07-14   引用
收下,慢慢学习。
19 楼 mercyblitz 2010-07-13   引用
RednaxelaFX 写道
well……有有同学想自己计算HotSpot里Java对象的布局的话,请参考HotSpot自身所使用的逻辑:
hotspot/src/share/vm/classfile/classFileParser.cpp
找这个方法:
ClassFileParser::parseClassFile(),看2833行开始:
C++代码 
// Field size and offset computation 

对象布局的计算就在里面,包括instanceKlass自身的布局(包含有Java类的静态变量)和Java对象实例的布局都在这里算。


谢谢,尽管我没有过(最近也在研究JVM实现),但是我比较偏好HotSpot的实现。不过我打算再写一篇计算篇,通过反向推到出来。

18 楼 RednaxelaFX 2010-07-13   引用
well……有同学想自己计算HotSpot里Java对象的布局的话,请参考HotSpot自身所使用的逻辑:
hotspot/src/share/vm/classfile/classFileParser.cpp
找这个方法:
ClassFileParser::parseClassFile(),看2833行开始:
C++代码 
// Field size and offset computation 

对象布局的计算就在里面,包括instanceKlass自身的布局(包含有Java类的静态变量)和Java对象实例的布局都在这里算。
17 楼 RednaxelaFX 2010-07-13   引用
其实在32位HotSpot里java.lang.Integer和java.lang.Long的大小就是一样的啊……
原因是32位HotSpot的对象分配是8字节对齐的。当对象自身所需数据占不足8字节的倍数的时候就会在一些位置加上padding。“一些位置”一般是整个对象的末尾,也可能是在中间。

HotSpot是按成员类型来计算对象布局的。可以读一下这篇,讲得还不错:Java Objects Memory Structure
(看不到的话请自行想办法翻……)
这帖讲的内容适用于32位HotSpot。其它JVM则有各自不同的对象布局方式。例如说早期的Sun JVM(后来叫Classic VM)采用的对象布局就跟HotSpot的完全不同,而像SableVM采用的对象布局则相当有特色。JVM规范并没有对对象布局做定义——JVM实现可以用自己喜欢的方式来做。

要靠谱的Java对象大小数据,可以参考这帖使用JVMTI来获取:Again about determining size of Java object
VisualVM也是通过JVMTI来获取对象大小、数量等的信息。

以前我也发过一帖是拿32位HotSpot的对象布局来玩的,[标题党] 跑得好好的C#程序咋移植为Java就不够内存用了呢?——忽悠一把
16 楼 mercyblitz 2010-07-13   引用
beneo 写道
mercyblitz 写道
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。


我没说清楚

你这个是用jprofile计算出来的,如果你那IBM VM看的话,可能就不一样。再次不同的VM版本,对象的大小也不一样。

我们在日常使用过程中,我自己只能估算一下,开多少数组,耗费多少内存。那究竟包含数组的对象耗费多少heap,你是不能计算出来的,除非拿jprofile等工具去实际测。


对于不同的JVM的实现,这个没有关系,最多是多测试而已。
15 楼 beneo 2010-07-13   引用
mercyblitz 写道
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。


我没说清楚

你这个是用jprofile计算出来的,如果你那IBM VM看的话,可能就不一样。再次不同的VM版本,对象的大小也不一样。

我们在日常使用过程中,我自己只能估算一下,开多少数组,耗费多少内存。那究竟包含数组的对象耗费多少heap,你是不能计算出来的,除非拿jprofile等工具去实际测。
14 楼 mercyblitz 2010-07-13   引用
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。
13 楼 beneo 2010-07-13   引用
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道
分享到:
评论

相关推荐

    [转载] JAXB中Java对象和XML之间的转换

    JAXB,全称为Java Architecture for XML Binding,是Java平台中用于在XML和Java对象之间进行映射的标准技术。它提供了一种自动的方式,使得开发者能够方便地在XML文档和Java对象之间进行数据交换,极大地简化了XML...

    通过JavaCompiler进行编译java文件(转载)

    在Java编程环境中,有时我们需要在运行时动态地编译Java源代码并生成对应的字节码文件,以便程序可以根据用户输入或其他条件动态加载和执行。Java Compiler API,即`javax.tools.JavaCompiler`,提供了这样的功能,...

    Json-RPC for java Example

    json-rpc-for-java,是仅仅不到100行的javascript代码和不到10个java文件实现的超级轻量级的通过 javaScript快速调用java对象并返回任意对象的轻量级框架,并且支持级联调用,也就是说不需要额外 的JavaScript编程,...

    Java面向对象的三大特性和面向对象的进阶

    主要简述Java面向对象基础,进阶,高级;从封装、继承、多态、接口、内部类、抽象类和代码块 适合刚开始的新手参考,毕竟我也是自学的 主要可以使用在计算机刚入门的小伙伴或者回来看一看复习一下简单的基础的 ① 凡...

    java多线程扫描器(转载)

    Java多线程扫描器的开发旨在深化对Java面向对象程序设计的理解,特别是巩固Java语言的语法规范。此项目的核心目标在于分析系统的可行性,明确开发方向,确保开发过程的合理性。它要求开发者能够精确识别系统流程,...

    JAVA数据库连接大全-转载

    这篇博客"JAVA数据库连接大全-转载"可能详细介绍了在Java中如何管理和使用数据库连接,包括使用JDBC API,数据库连接池以及相关的工具。这里我们将深入探讨JDBC、数据库连接池的重要性和配置方法。 1. **JDBC基础**...

    转载-Java语言入门教程

    完成这些步骤后,初学者就可以开始他们的Java编程之旅,学习基本语法、数据类型、控制结构、类和对象等核心概念,并通过编写简单的程序实践这些知识。 总的来说,Java语言入门教程是一个为初学者设计的引导性教程,...

    java文件路径操作详细(转载)

    Java文件路径操作是Java开发中常见且重要的任务,无论是在读写文件、处理资源还是进行系统交互时,都需要对文件路径进行操作。本篇将详细探讨Java中的文件路径处理技术,主要涵盖以下知识点: 1. **File类**: ...

    重新认识java-1909jsd林芳钦.pptx

    1. Java对象下面将深入了解什么是对象。 如果考虑现实世界,可以在我们周围找到许多对象,如:汽车,狗,人等等。所有这些对象都有状态和行为。 如果考虑一只狗,那么它的状态是 - 名称,品种,颜色,并且它的行为...

    (转载)文思创新 java开发工程师笔试题.doc

    创建新线程的起点是通过`Thread`类的`start()`方法或者实现`Runnable`接口并调用`Thread`对象的`start()`方法。在提供的选项中,正确的方法是D:`public static void main(String args[])`,因为`main`方法是Java...

    java面试资料(转载)

    多态性是Java中实现动态绑定的基础,它允许我们使用父类型的引用指向子类型对象,从而实现更加灵活的设计。面试官可能会询问抽象类和接口的区别,方法的重写和重载,以及如何利用多态优化代码。 日期和时间的处理是...

    java编程事项(转载收集整理版)

    2. **类与对象**:Java是基于类的,这意味着一切皆为对象。理解类的定义、构造函数、封装、继承和多态是Java编程的基础。同时,接口也是Java中实现多态性的重要方式。 3. **异常处理**:Java的异常处理机制是通过...

    利用 Java 实现组合式解析器,基于 Java 的界面布局 DSL 的设计与实现(转载)

    3. **Java 语言特性**:Java 的面向对象特性如何帮助设计和实现解析器,以及 Java 的标准库如 `java.util.regex` 在解析中的应用。 4. **DSL 设计**:如何定义一个简洁、直观的界面布局DSL,包括关键字选择、语法...

    深入分析 Java I/O 的工作机制(转载)

    Java将所有的I/O操作抽象为流对象,分为字节流和字符流两大类。字节流处理单个字节的数据,如InputStream和OutputStream;字符流处理Unicode字符,如Reader和Writer。 2. **流的方向** 流可以是输入流或输出流,...

    Agile Java (EN)

    书后是这样介绍的:掌握java5.0,面向对象设计和测试驱动开发。本书将三者编织在一起,形成一种旨在构建一种专业健壮的软件系统的统一连贯的方法。 Agile java 讲了一种我之前没有遇到的程序设计思想——测试驱动...

    JAVA OA平台源码(转载)SPRING BOOT....

    通过XML或注解的方式定义SQL查询,MyBatis会自动将结果映射到Java对象。在Spring Boot中,可以通过@MapperScan注解扫描Mapper接口,使用@Autowired自动注入SqlSessionTemplate或SqlSessionFactory。 4. **OA管理...

    网上转载JAVA面试基本大全

    在Java面试中,面试官通常会关注候选人的基础知识,特别是面向对象编程的概念、数据类型的使用、字符串处理以及异常处理等方面。以下是对这些知识点的详细阐述: 1. **面向对象的特征** - **抽象**:抽象是面向...

    人脸识别检测opencv简单java实现

    然后,调用分类器上的detectMultiScale方法传递给它图象和MatOfRect对象。这个过程之后,MatOfRect将有面部检测。 我们遍历所有的脸部检测并用矩形标记图像。 最后,将图像写入输出的 .png 文件里。 ---------------...

    java开发html转pdf示例(转载)

    在这个示例中,我们首先创建了一个iText的`Document`对象,然后通过`PdfWriter.getInstance()`创建了一个PDF写入器。接着,我们使用`CoreRenderer`来渲染HTML内容到PDF。请注意,你需要根据实际的HTML内容和输出文件...

    骆昊JAVA面试题全集2018博客文章整理

    文章在CSDN上的访问量超过5万次,并被多个网站和个人以原创形式转载,显示出其广泛的影响。随着Java 8的发布,骆昊继续更新题目,以涵盖新版本带来的变化。他不断整理和改进,结合学生们的面试经验,力求保持内容的...

Global site tag (gtag.js) - Google Analytics