Tuesday 1 December 2009

To calculate size of an object in java

In one of mine project work I was required to calculate the size of an object the best way that I found is this :
when I started r&d for this topic I came to know that in java 1.5 there is an api introduced i.e instrumentation agent by using that we have a premain method which is called by the JVM even before the void main. so by storing the information of objects in jvm and then calculating the size was the core concept to know the memory utilization of an object. I know theortically it is very easy to write. Well I wont disappoint you .. practically as well :) here is the step by step approach to calculate the memory consumed by an object in your application :

First thing that is required is to have a class in your project which contains premain method the sample class is shown below:-


package com.memory;


import java.lang.instrument.Instrumentation;


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


Second thing to do is to keep in mind that this application has to be run on command prompt and you have to make a jar of your project which will be having above mentioned class.  class name and package  are your choices.
While making jar of your project you have to write this line (Premain-Class: ****.rc.postpay.controller.MyAgent) in your manifest file. This is mandatory the class name and package name varies according to your comfort and requirement.

well half of the work is done now next task is to recusively calculate the size . For that use the code shown below:

  /**
          * 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 visited = new IdentityHashMap();
                 Stack stack = new Stack();              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 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 stack, Map visited) {              if (skipObject(obj, visited)){                  return 0;              }              visited.put(obj, null);                            long result = 0;              // get size of object + primitive variables + member pointers               result += MyAgent.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;           }  } Put all these methods in MyAgent class as these methods needs the instance of Intrumentation agent.Call the method [ MyAgent.fullSizeOf(o);] where o is an object for whom every one is concerned. to write in a file or to print on the console you can write custom methods like the one shown below. You can call this below mentioned method wherever you need to know the size of your object and this method will print the size on console and write in file as well.     public static void measureSize(Object o, String filename)     {         System.out.println("inside measure size");         long sizeInBytes = MyAgent.fullSizeOf(o);         double sizeInKB = (sizeInBytes / 1024);         double sizeInMB = (sizeInKB / 1024);         try         {             // Create file             FileWriter fstream = new FileWriter("c://MemoryUsageForReferenceData", true);             BufferedWriter out = new BufferedWriter(fstream);             out.write(filename + "," + sizeInKB + "," + sizeInMB +"\n");             // Close the output stream             out.close();         }         catch (Exception e)         {// Catch exception if any             System.err.println("Error: " + e.getMessage());         }         // long size = MyAgent.getObjectSize(o);         /*          * System.out.printf("size =%d", size);          */         // int mapSize = o.size();         // System.out.println("map size" + mapSize);         System.out.println("size --- " + sizeInKB);     } Well we are almost done . The only thing remaining is to run this application on command prompt. Firstly I will summarize then we will run. Make one class Like My Agent put all the methods which are shown below put this class in your project and make the jar having manifest file which contains [Premain-Class: uk.co.o2.rc.postpay.controller.MyAgent]  package and class name depends on you premain-class is mandatory dont play with that. Thats it we are done now open your command prompt and run your jar by this command. Set all the classpath that you need for your project . Then execute this command java  - javaagent:[yourJar name] [the class name which contains the main Method] for example : java -javaagent:c:/memoryAgent.jar com.memoryagent.Test Done :) please post queries for anything confusing/wrong

No comments:

Post a Comment