/*
 * @(#)Sizeof.java 1.0  2001-10-19
 *
 * Copyright (c) 2001 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.debug;
// ----------------------------------------------------------------------------
/**
 * A simple class to experiment with your JVM's garbage collector
 * and memory sizes for various data types.
 *
 * @author <a href="mailto:vlad@trilogy.com">Vladimir Roubtsov</a>
 */
public class Sizeof
{
    public static void main (String [] args) throws Exception
    {
        // "warm up" all classes/methods that we are going to use:
        runGC ();
        usedMemory ();
        
        // array to keep strong references to allocated objects:
        final int count = 10000; // 10000 or so is enough for small ojects
        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;
            
            // INSTANTIATE YOUR DATA HERE AND ASSIGN IT TO 'object':
            
            object = new Object (); // 8 bytes
            //object = new Integer (i); // 16 bytes
            //object = new Long (i); // same size as Integer?
            //object = createString (10); // 56 bytes? fine...
            //object = createString (9)+' '; // 72 bytes? the article explains why
            //object = new char [10]; // 32 bytes
            //object = new byte [32][1]; // 656 bytes?!
          
            if (i >= 0)
                objects [i] = object;
            else
            {
                object = null; // discard the "warmup" 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");
    }
    
    // a helper method for creating Strings of desired length
    // and avoiding getting tricked by String interning:
    public static String createString (final int length)
    {
        final char [] result = new char [length];
        for (int i = 0; i < length; ++ i) result [i] = (char) i;
        
        return new String (result);
    }

    // this is our way of requesting garbage collection to be run:
    // [how aggressive it is depends on the JVM to a large degree, but
    // it is almost always better than a single Runtime.gc() call]
    private static void runGC () throws Exception
    {
        // for whatever reason 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 < 1000); ++ 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
// ----------------------------------------------------------------------------
