1

In Java, I want to figure out the exact amount of memory a object uses while allocated.

Just calling the constructor and measure won't work, as it might allocate other object during the constructor. Also, I prefer to use a method that realtime calculate used memory in the given VM. This might not be the standard VM so counting fields and making a smart guess is not sufficient.

Anyway, so far I found that you can make a object with no other allocations with the newConstructorForSerialization found in sun.reflect.ReflectionFactory.

This works, but somehow the call to newInstance allocate 1 block of memory more then expected.

For example, the class

public class a {
    Integer a;
}

and

public class b {
    Integer b = new Integer(12345);
}

Should both give the same result. 16 bytes in this case using Java 7 in the default VM.

However, my code gives 32 bytes (always 16 more then expected). I can compensate for this by removing 16 from the result, but I need to be 100% sure it always allocate that additional block. It's more important for me to know the upped bound of memory usage then the exact amount. So it's only safe to subtract 16 from the result if I'm 100% sure this block is always added.

My code: (run with -XX:-UseTLAB VM arguments)

import java.lang.reflect.Constructor;

import sun.reflect.ReflectionFactory;

public class test {

    public static void main(String[] args) throws Exception {
        prepare(a.class, Object.class);
        System.out.println(memUse());
        System.out.println(memUseSimple());
    }

    private static long memUseSimple() {
        long start = Runtime.getRuntime().freeMemory();
        a a = new a();
        return start - Runtime.getRuntime().freeMemory();
    }

    private static long memUse() throws Exception {
        Object o0 = intConstr.newInstance();
        long start = Runtime.getRuntime().freeMemory();
        Object o1 = intConstr.newInstance();
        return start - Runtime.getRuntime().freeMemory() - 16;
    }

    private static Constructor<?> intConstr;

    private static void prepare(Class<?> clazz, Class<?> parent) throws Exception {
            intConstr = ReflectionFactory.getReflectionFactory()
                    .newConstructorForSerialization(clazz,
                            parent.getDeclaredConstructor());
            return;
    }
}

Edit: To clarify: I want to know why i need to subtract the 16 bytes overhead of the intConstr.newInstance() call, and if i can be 100% sure this overhead is always the same (or at least not less then 16 bytes).

Even if you replace a with b in the above code, it still gives 16 as the result for memUse(), but not memUseSimple(). I only care about memUse() but added the simple method as a comparison.

I know intConstr.newInstance() can have a different overhead on another VM. This is not important, what i need to know if that if it gives a overhead of 16 bytes on the current VM, will it always give 16 bytes overhead (during this runtime)? Also, where does this overhead come from compared to just new a()?

Dorus
  • 7,276
  • 1
  • 30
  • 36
  • Objects have overhead. Here's a related question: http://stackoverflow.com/questions/258120/what-is-the-memory-consumption-of-an-object-in-java – parsifal Jan 15 '13 at 19:41
  • Try changing you init to `new Integer(0)`; values -128 to 127 are cached, so that would be a fairer comparison. – Bohemian Jan 15 '13 at 19:45
  • Did you called GC before calculating memory usage? – Andremoniy Jan 15 '13 at 19:48
  • also, how can you possibly assume that only that single object allocation will be reflected in the change in free memory? – jtahlborn Jan 15 '13 at 19:51
  • The code should work for both 32 and 64bit jvm. I noticed the overhead was 32 bytes in another jvm that used 32 bytes memory blocks. – Dorus Jan 15 '13 at 19:53
  • Ofcourse other allocations (for example, on another thread) can be done, but we can ignore that here, i will use other means that are out of the scope of this question to work around that. (If you really need to know, this code will be run in a jvm launched just for the purpose of finding the object memory usage, there will be no other threads). – Dorus Jan 15 '13 at 19:55
  • @Bohemian: class b is not used here, even if it was, memUse() will still return 16 as the new Integer is not allocated. – Dorus Jan 15 '13 at 19:56
  • @Andremoniy: Calling the GC before intConstr.newInstance(); does not change it's memory consumption. – Dorus Jan 15 '13 at 19:58
  • you do realize the size of a reference is different on a 32 and 64 bit system, right? (and ever more different if you have compressed OOps enabled). – jtahlborn Jan 15 '13 at 19:59
  • Yes, and this question is not about object size, it is about the overhead in `intConstr.newInstance()` vs `new a()`. I realize that if I want to know the size of `a` in a 32 bit VM i better run this code in a 32 bit VM. Compressed OOps and other optimisations are also out of scope here. I know about them but i'de like to focus on the overhead on `intConstr.newInstance()` here. – Dorus Jan 15 '13 at 20:07
  • you can't determine what is "overhead" without determing what the expected size is. i'm trying to understand how you are determining expected size. one facter in this calculation is the bit-ness of the jvm, hence my question. also, do you know if the jvm has a minimum allocation size? – jtahlborn Jan 15 '13 at 20:10
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/22778/discussion-between-dorus-and-jtahlborn) – Dorus Jan 15 '13 at 20:12
  • have you already looked at [this answer](http://stackoverflow.com/questions/52353/in-java-what-is-the-best-way-to-determine-the-size-of-an-object)? – jtahlborn Jan 16 '13 at 13:06

1 Answers1

1

The thing is more or less simple. I used the following code to check if there's a difference between new and newInstance() and I could not find any (Java 7, 64 bit, -XX:-UseTLAB -verbose:gc):

public final static long usedMemory()
{
    return Runtime.getRuntime().totalMemory() 
               - Runtime.getRuntime().freeMemory();
}

public static void main(String[] args) throws Exception
{
    // we get the default ctor
    Constructor<?> ctor = ReflectionFactory
            .getReflectionFactory()
            .newConstructorForSerialization(
                    Main.class, Object.class.getDeclaredConstructor());
    // warm up newInstance
    Object d = ctor.newInstance();
    // warm up Runtime
    System.out.println(usedMemory());
    // warm up Main
    Object b = new Main();
    // force GC
    System.gc();
    // get currently used memory
    final long mem = usedMemory();
    Object a = new Main();
    System.out.println(usedMemory() - mem);
    Object c = ctor.newInstance();
    System.gc();
    System.out.println(usedMemory() - mem);
    System.out.println(a + ", " + b + ", " + c + ", " + d);
}

The code loks like this because we have to trick the compiler not to optimize away any portions of it and to let the system initialize some background stuff. The output I get is:

384024
[GC 381K->400K(249664K), 0.0006325 secs]
[Full GC 400K->264K(249664K), 0.0040675 secs]
16
[GC 264K->296K(249664K), 0.0002040 secs]
[Full GC 296K->264K(249664K), 0.0023534 secs]
32

... which is exactly what I had expected. One object of type 'Main' consumes 16 bytes and if I allocate another we're consuming 32 bytes. The second value is the difference to the used memory value before the first 'Main' allocation.

It also did not made any difference if the Main class contained a member variable or not. I tried it without, with Integer integer; and Integer integer = Integer.of(42);, the result was always the same.

The overhead you encounter comes from the stuff that's going on in the background of the newInstance call. In my example I compensated for this by enforcing the GC to run.

This may be a little cheating, but this example is just to show that there's now difference between new and newInstance except that the latter allocates some more stuff in the background. As these objects get allocated in eden space, and won't survive any GC cycle they can be neglected IMHO.

Neet
  • 3,937
  • 15
  • 18
  • 1
    the OPs code isn't using the "normal" `newInstance()` call, it is using a special one built for internal serialization support. – jtahlborn Jan 15 '13 at 20:46
  • I updated the code to use the same ctor as the OP, see for yourself. It does not make any difference. – Neet Jan 15 '13 at 20:50
  • I ran this against some testcases: `4 Class d reserved -112 bytes.` While correct, this is also a risky approach since now even more unknown code is ran. In some cases the GC even removes more the second run as you can see. This answer does make me realize i better find a way to suppress the GC during `memUse()`. – Dorus Jan 15 '13 at 20:57
  • Yeah, it is always hard to tell what the GC does and how many objects get allocated behind the scenes. But using such a simple class, the way you construct (allocate) the object does not make any difference. – Neet Jan 15 '13 at 21:00
  • 1
    Dorus, there is a lot of standard knowledge you need to acquire when measuring memory. One `System.gc` is usually not enough because it triggers only a minor collection; two usually are; I do three-five for good measure, with pauses between them in case there is a concurrent GC policy in effect. Memory usage is taken as `totalMemory-freeMemory` otherwise heap resizing will mess up your result. Thread-local allocation buffers (TLAB) need to be disabled or else you must measure not one, but one million allocations. – Marko Topolnik Jan 15 '13 at 21:00
  • @MarkoTopolnik I ran the code above using `-verbose:gc`. As you can see `System.gc()` triggers a minor _and_ a major GC (at least on my system). – Neet Jan 15 '13 at 21:05
  • @Neet That's good to know; it is of course highly volatile and changes between minor Java versions. My experience, though, has been consistent in that only the second `System.gc` call causes the memory usage to stabilize. – Marko Topolnik Jan 15 '13 at 21:07
  • @MarkoTopolnik yes, you're right. The whole GC thing is implementation dependent. I bet it also runs differently on different OS'es. I hope the example is simple enough that there's not much going on in the background so that one GC call is sufficient ... but you never can tell. – Neet Jan 15 '13 at 21:09
  • @Neet the main point is trust nothing but hard data: your results must be repeatable across many runs and across different allocation counts. It is **always** better to use an array of objects you are measuring and vary its size across orders of magnitude: 10, 100, 1000, 1000000. At all sizes, bytes per object must be invariant; else you have a loophole in your measuring method. – Marko Topolnik Jan 15 '13 at 21:11
  • This discussion is fun and all, but it doesn't feel like running the gc a arbitrary number of times will give me a 100% certain result. I need to find the size of the current object 100% accurate for academic reasons, and "Here i run the GC 5 times in a row to really sure nothing else is going to be cleaned except the overhead of my next call" is just not going to be accepted. – Dorus Jan 15 '13 at 21:11
  • @Dorus It's the best you can get. **Not** running GC at all is definitely not helping because you don't distinguish retained from ephemeral allocations. As I explain, repeatable results across many orders of magnitude **are respectable**. – Marko Topolnik Jan 15 '13 at 21:13
  • If there is any way to determinate if the ephemeral allocations are fixed, i can just compensate for them. Even if it allocate more temp space once in a while that's not a issue. I only need to make sure i never end up with a too low estimate. Thinking about it i might end up both algorithms multiple times and accept the result if the results are equal. Thanks for thinking with me about this :) – Dorus Jan 16 '13 at 13:22