• 获取JAVA对象占用的内存大小


    获取JAVA对象占用的内存大小

     

      介绍两种获取JAVA对象内存大小的方法。

    第一种:Instrumentation

    简介:

    使用java.lang.instrument 的Instrumentation来获取一个对象的内存大小。利用Instrumentation并且通过代理我们可以监测在JVM运行的程序的功能,它的原理是修改方法的字节码。

    首先创建代理类
     
    复制代码
    package com.dingtongblog.size;
    import java.lang.instrument.Instrumentation;
     
    public class ObjectSize {
        private static volatile Instrumentation instru;
     
        public static void premain(String args, Instrumentation inst) {
            instru = inst;
        }
     
        public static Long getSizeOf(Object object) {
            if (instru == null) {
                throw new IllegalStateException("Instrumentation is null");
            }
            return instru.getObjectSize(object);
        }
    }
    复制代码
         premain方法:JVM会首先调用这个方法。通过这个方法我们就可以把属性instru初始化化成功,通过Instrumentation的getObjectSize(Object object)方法我们就获取一个对象的大小了。
     
    然后把这个类打包成jar包
    首先我们要创建manifest.txt,并且增加这样的一行
    Premain-Class:com.dingtongblog.size.ObjectSize
    这个premain-Class指定了是哪个是代理类,也就是包括了premain方法的类。
    然后把ObjectSize打包成jar包
     
    java -cmf manifest.txt simpleSize.jar com/dingtongblog/size/ObjectSize.class 

    运行

    然后把jar引入到工程中, 并且启动参数加入
    -javaagent:jarpath[=options]
    在命令行中执行
    java -javaagent:simpleSize.jar TestMain

    (当前TestMain和simpleSize.jar在同一目录下)

    测试代码如下:

    复制代码
    import com.dingtongblog.size.ObjectSize;
    public class TestMain {
    public static void main(String[] args) {
    String a = new String(aa);
    System.out.println(ObjectSize.getSizeOf(a));
    }
     
    }
    复制代码
    输出24
     
    我们修改参数a
    String a = aa; 改成
    String a = aaaa;
     
    然后再运行main,还是输出24。
     
        为什么值会没有改变呢?这里虽然a指向的对象已经改变了,但是输出的值还是24没变。这是因为这个使用getSizeOf这个方法取得的对象本身的内存大小,不包含对象中属性所指向的对象的大小。在String 中一共有3个int 属性,1个数组的引用,再加上对象头占的字节数为 3 * 4 + 4 +8 = 24 ,然后24正好是8的倍数,不需要填充字节。所以直接输出24。
     
    有办法可以取到一个对象完整的字节数吗?有一种思路是通过反射遍历对象中每个属性,然后调用上述的方法得到每个对象大小,把得到得对象再重复上面的过程,直到最后指向的是基本类型。直接引用这篇文章中的jar包。
     
    引入包,并且启动JVM的时候加上参数
    -javaagent:D:sizeofag.jar

    测试代码:

    复制代码
    public class TestMain {
      
        public static void main(String[] args) throws IllegalAccessException {
      
            String a = new String(aa);
            System.out.println(SizeOfAgent.fullSizeOf(a));
            System.out.println(SizeOfAgent.sizeOf(a));
      
    }
    复制代码

    可以看到输出的结果40,24

     
    40就是这个对象的完整大小。首先是前面计算的24字节+数组对象头的12个字节+两个字符字节2*2 可以得到24+12+4 =40。40是8个倍数,不需要填充字节。
     
    第二种是jmap ,jhat命令
       jmap 可以输出给定的程序中堆的详情。
    jmap -histo <pid>  (pid为当前JAVA进程的id)
    例如
    jmap -histo 20230
    这样就直接输出当前堆的详细情况,但是这样不太直观。
     
    通过
    jmap -dump:format=b,file=<filename> 

     

    可以把java的堆以hprof 二进制格式输出到一个文件中,然后通过jhat命令来查看,jhat会生成一个页面,能比较直观的查看堆详情。但是jhat需要的内存空间为dump文件的几倍,如果dump文件比较大会遇到OOM错误,这时候可以通过MAT来浏览堆信息。

    例如
    jmap -dump:format=b,file=d:dump.txt
    然后通过
    jhat filename ;

      (filename为之前dump出来的文件) 会解析JAVA 堆的DUMP文件并且会启动一个web服务器,服务器的默认端口为7000,命令执行完之后就可以通过127.0.0.1:7000访问堆详情了。

    jhat d:dump.txt
    可以得到类似这样的页面。
    jhat
     
    不过通过这个方法得到的计算结果和之前用的instrumentation方法得到的结果还不一样。这个地方可能计算的方法有区别,个人觉得instrumentation的结果会比较准,但是还没找到有关的资料说明这个问题。
    另外使用Jprofiler工具也可以监控内存使用的详细情况。
    资料:
  • 相关阅读:
    ASP.NET 页面间数据传递的方法
    ASP.NET中实现页面间数据传递的方法
    C# 连接SQL数据库
    C# 通过url地址获取页面内容
    JS弹窗带遮蔽的功能
    C# Code First 实例学习
    CS窗体程序数据列表分页
    关于RDLC报表打印预览界面显示页码问号的问题
    C#Dictionary键值对取值用法
    分别获取一个字符串中的字母和数字
  • 原文地址:https://www.cnblogs.com/firstdream/p/8274387.html
Copyright © 2020-2023  润新知