分析java对象内存占用

原创 2015年07月08日 11:44:15

JVM的分代GC法非常适合处理小而短命的对象,这类对象可以很快的在minor GC后清除;但是日常开发中经常需要缓存一些对象,这些对象在经过几次MinorGC后最终会进入老年代;如果这些对象占用太多的内存则会出现OOM,所以应该限制缓存对象集合的内存占用,这样我们就需要计算出每个对象所占用的内存。

怎样计算一个对象的内存呢? 首先需要明白对象的内存结构。
对象的内存结构分为三大块,Head,Body和Padding。

  • Head:

    • class引用:Object中定义getClass()方法来获取对象的Class对象,大家知道class对象是保存在方法区的,也就是说对象中保存着指向class对象的引用,这个引用就在Head中。

      一个引用在32位系统中占用4byte,在64位系统中占用8byte

    • MarkWord :Head中还存放一些其他信息:是否加锁,GC标志位,MinorGC次数,对象hashcode等,这部分空间通常叫作叫MarkWord(32位系统中占用4byte,在64位系统中占用8byte)。

    • 如果对象是数组,head中还存储这数组的长度(4byte)
  • Body:就是对象非static属性所占的空间 (static属性存放在方法区)
  • Padding:java对象以8字节对齐在内存中,如果对象的占用不是8字节的倍数,则会补齐为8的倍数。-padding就是用来补齐的空间

知道了对象的内存结构,就可以计算出对象的内存占用
以一个64位系统中的Integer为例,
1. 计算Head区,8+8=16 (byte),
2. 计算Body区,查看Integer 的源码发现Integer类中只存在一个非static属性 private final int value; 所以Body占4字节,
3. 加入Padding,这样head+body=16+4=20,计算出的位数并不是8的倍数,所以加入4byte padding凑齐。
这样下来一个Integer占用的内存为Header+Body+Padding=16+4+4=24(byte)

对于Integer这种非常简单的类计算非常简单,如果类中存在继承关系,还需计算父类的属性。

这样计算太麻烦了,还好从JDK1.5以后引入了一个Instrumentation接口,调用

Instrumentation#getObjectSize(Object objectToSize)

就可以轻松的计算一个对象的内存大小。

下面是一个对于Instrumentation 对象的封装

/**
 *  Instrumentation agent used 
 *  from : http://www.jroller.com/maxim/entry/again_about_determining_size_of
 */
public class SizeOfAgent {

    static Instrumentation inst;

    /** initializes agent */
    public static void premain(String agentArgs, Instrumentation instP) {
        inst = instP;           
    }

    /**
     * Returns object size without member sub-objects.
     * @param o object to get size of
     * @return object size
     */
    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);
    }

    /**
     * Calculates full size of object iterating over
     * its hierarchy graph.
     * @param obj object to calculate size of
     * @return object size
     */
    public static long fullSizeOf(Object obj) {
        Map<Object, Object> visited = new IdentityHashMap<Object, Object>();
        Stack<Object> stack = new Stack<Object>();

        long result = internalSizeOf(obj, stack, visited);
        while (!stack.isEmpty()) {
            result += internalSizeOf(stack.pop(), stack, visited);
        }
        visited.clear();
        return result;
    }               

    private static boolean skipObject(Object obj, Map<Object, Object> visited) {
        if (obj instanceof String) {
            // skip interned string
            if (obj == ((String) obj).intern()) {
                return true;
            }
        }
        return (obj == null) // skip visited object
                || visited.containsKey(obj);
    }

    private static long internalSizeOf(Object obj, Stack<Object> stack, Map<Object, Object> visited) {
        if (skipObject(obj, visited)){
            return 0;
        }
        visited.put(obj, null);

        long result = 0;
        // get size of object + primitive variables + member pointers 
        result += SizeOfAgent.sizeOf(obj);

        // process all array elements
        Class clazz = obj.getClass();
        if (clazz.isArray()) {
            if(clazz.getName().length() != 2) {// skip primitive type array
                int length =  Array.getLength(obj);
                for (int i = 0; i < length; i++) {
                    stack.add(Array.get(obj, i));
                } 
            }       
            return result;
        }

        // process all fields of the object
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (!Modifier.isStatic(fields[i].getModifiers())) {
                    if (fields[i].getType().isPrimitive()) {
                        continue; // skip primitive fields
                    } else {
                        fields[i].setAccessible(true);
                        try {
                            // objects to be estimated are put to stack
                            Object objectToAdd = fields[i].get(obj);
                            if (objectToAdd != null) {                        
                                stack.add(objectToAdd);
                            }
                        } catch (IllegalAccessException ex) { 
                            assert false; 
                        }
                        }
                    }
            }
            clazz = clazz.getSuperclass();
        }
        return result;
    }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

Java线上应用故障排查之二:高内存占用

前一篇介绍了线上应用故障排查之一:高CPU占用,这篇主要分析高内存占用故障的排查。 搞Java开发的,经常会碰到下面两种异常: 1、java.lang.OutOfMemoryError: ...
  • lao_pei
  • lao_pei
  • 2016年01月31日 20:30
  • 5126

Java对象内存占用分析

原文地址:https://segmentfault.com/a/1190000006933272 本文深入分析并验证了不同Java对象占用内存空间大小的情况。对于不同的jvm实现,Java对象占...
  • codershamo
  • codershamo
  • 2016年09月21日 10:20
  • 4073

JAVA查看内存使用情况

一、使用JAVA类库。
  • jingguangzhilu
  • jingguangzhilu
  • 2014年06月05日 11:01
  • 5424

Java计算一个对象占用内存的大小

在C/C++中计算某一个基本类型或者对象占用内存大小的方法很简单,只要调用库里面的sizeof()操作符即可,但是在Java的API里面并没有给我们提供类似的方法。那么我们可不可以自己实现一个Java...
  • bobpauline
  • bobpauline
  • 2014年03月07日 17:35
  • 5769

Java内存优化和性能优化的几点建议

转载地址http://www.open-open.com/lib/view/open1399884636989.html 1.没有必要时请不用使用静态变量     使用Ja...
  • l_215851356
  • l_215851356
  • 2017年01月17日 11:26
  • 2154

JAVA进程占用高内存原因分析与优化方法

首先看一下一个java进程的jmap输出:  代码如下   [lex@chou ~]$ jmap -heap 837 Attaching to proces...
  • u012251421
  • u012251421
  • 2015年09月28日 09:51
  • 4624

Java内存使用情况查看工具

Java通过jvm自己管理内存,同时Java提供了一些命令行工具,用于查看内存使用情况。 这里主要介绍一下jstat、jmap命令以及相关工具。...
  • shelldon
  • shelldon
  • 2017年01月07日 00:11
  • 4482

JAVA进程占用高内存原因分析与优化方法

首先看一下一个java进程的jmap输出: 代码如下: [lex@chou ~]$ jmap -heap 837 Attaching to process ID 837, please wait...
  • u014022631
  • u014022631
  • 2017年06月20日 16:10
  • 617

JAVA进程内存用量高的分析与解决

首先看一下一个java进程的jmap输出: [lex@chou ~]$ jmap -heap 837 Attaching to process ID 837, please wait... Debu...
  • Xiao_Qiang_
  • Xiao_Qiang_
  • 2014年12月10日 18:49
  • 4261

Java线上应用故障排查之二:高内存占用

前一篇介绍了线上应用故障排查之一:高CPU占用,这篇主要分析高内存占用故障的排查。 搞Java开发的,经常会碰到下面两种异常: 1、java.lang.OutOfMemoryError: ...
  • blade2001
  • blade2001
  • 2013年06月09日 16:45
  • 18260
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:分析java对象内存占用
举报原因:
原因补充:

(最多只允许输入30个字)