Adam Bien's Weblog

Thursday Feb 24, 2011

How Big Is An Java Object?

Smaller, than you may think. The class BigEntity:


public class BigEntity {

    private long id;
    private String name;
    private String secondName;
    private String description;
    private Date date;
    private int number;

    public BigEntity() {
        this.id = System.currentTimeMillis();
        this.name ="duke" + this.id;
        this.secondName = "java" + this.id;
        this.description = "desc" + this.id;
        this.date = new Date();
        this.number = 42;
    }
}


...was instantiated 50.000 times with:

public class HowBigIsAnObject {
   public final static List list = new ArrayList();
    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<50000;i++){
            list.add(new BigEntity());
        }
    }
}


The result is: 50,000 instances consume 3,200,000 bytes (~3MB). One instance takes 64 bytes. The size was measured with: visualvm.


NEW workshop: Microservices with Java EE 7 and Java 8, January 26th, 2015, Airport Munich

A book about rethinking Java EE Patterns

Comments:

Hi, is this on 32bit or 64bit JVM?

Posted by Christian Kalkhoff on February 24, 2011 at 02:30 PM CET #

@Christian,

forgot to mention that. It was: Java HotSpot(TM) 64-Bit Server VM (build 17.1-b03-307, mixed mode)

thanks for the hint,

adam

Posted by adam-bien.com on February 24, 2011 at 02:43 PM CET #

Such measurement strongly depends on the state of your attributes. Date for example holds a private transient BaseCalendar.Date cdate; which will add some bytes whenever set.

Posted by anonymous on February 24, 2011 at 03:25 PM CET #

Strange it seems, but I see 1,800,000 bytes for 50,000 instance in VisualVM. I am using JDK6 in Windows7 32bit.

Any idea whats happening ?

Posted by Shaiju B on February 24, 2011 at 03:47 PM CET #

@Adam,@Shaiju:

How many millis did it take to create the 50,000 objects?

Posted by Jörg on February 24, 2011 at 04:00 PM CET #

8 bytes for the class overhead
8 for the long
4 for the int

8 for the 64bit reference (or 4 in case of a 32bit runtime)

and the Strings? If each really is allocated, it should end up much more - 72(?) bytes alone for a single 17-character String. Maybe the JVM is optimizing? By sharing identical Strings? But then again it probably should end up much less than the 64 bytes, possibly even less than the 36 of Shaiju?

Posted by Jörg on February 24, 2011 at 04:13 PM CET #

JVM do not do optimization of runtime created strings. There are const and shared strings "duke", "java", "desc", but "duke" + ... creates unshareble string.

In any way above calculations are correct. String e.g. contains 3 ints, and one pointer.

Depending on GC implementation, each object may be referenced on other list, may have (somewhere) header for marking to sweep, or number of references number.

Memory reported by JVM doesn't describe true memory needed for one object. Actually, determining true memory usage is hard (JVM may not free memory to system, even heap goes from 300MB to 3MB).

Btw, JVM may pack pointers, but it is useless on bigger systems.

Posted by 80.48.239.196 on February 24, 2011 at 05:49 PM CET #

And on a related note, NetBeans has a library called INSANE which can be used to create unit tests that ensure your application does not have any memory leaks. Tor Norbye did a good job of explaining it here:

http://tornorbye.blogspot.com/2010/04/how-to-write-memory-leak-unit-test.html

Posted by Tom Wheeler on February 24, 2011 at 07:22 PM CET #

Yes, this isn't helpful without detail. I don't see how this can be 64 bytes.

8 for class overhead
8 for the long
8 for each of the Strings and the Date. (thus 8 * 4)
4 for the int.

That, alone, is 52.

The strings are, what, 14 characters? Milliseconds is in the billions? that means each string is 28 bytes (UTF-16). * 3 = 84 bytes for array storage. Now we're up to 136. Then add in the object overhead and other internal members for each of the Strings and the Date.

Strings have 3 integers and a char array reference. 3 * 4 + 8, 20 bytes. Plus object overhead, means 28 per String. I don't know what the overhead of an array is, but it IS an object, so it has the object overhead (8), probably a count (an int, likely, another 4), then the data (if that's a pointer, it's another 8). So, conservatively, that's another 20 bytes just for the char array overhead. That bumps the String up to 48 bytes.

So, String with 14 characters are 48 + 28, 76 bytes total.

Dates have the time (long, 8 bytes), and a pointer to a Calendar, 8 bytes, plus object overhead - total 24.

3 Strings + 1 Date is 228 + 24 = 252 bytes per instance extra.

Now a fully realized instance is 52 + 252 = 304.

So if someone can explain how 304 gets crushed down to 64 I'd be appreciative. Or, just tell me how my math is wrong, that would be fine too.

Posted by Will Hartung on February 25, 2011 at 01:27 AM CET #

@Jörg
It took 5 millisecs to execute.

Posted by Shaiju B on February 25, 2011 at 08:51 AM CET #

Here is an excellent article on this subject by Dr. Heinz Kabutz (javaSpecialists.co.za): http://www.javaspecialists.eu/archive/Issue142.html .

Determining the actual size of an Object at runtime is not as simple as it sounds.
However Dr. Kabutz' article and the series of articles leading up to the latest one show concisely how to determine the size of an object at runtime and what needs to be taken into account when doing so.

Posted by Andy B on February 25, 2011 at 11:46 AM CET #

@Will,

Joerg
(http://www.adam-bien.com/roller/abien/entry/how_big_is_an_java#comment-1
298556831704) has a good explanation of the size. The content of the String
does not belong to the size of the instance, rather than its pointer.

thanks for your precise calculations!

adam

Posted by adam-bien.com on February 25, 2011 at 12:18 PM CET #

@Andy,

the precise calculation is indeed hard. Particularly with the flyweight implementation of String inside the JVM,object reuse and "hidden" references to lazy loading mechanism of e.g. JPA implementations, or EJB / CDI aspects.

visualvm / heapdumps, however, are a very good tool for a rough estimation of e.g. session size etc.

thanks for the pointers!

adam

Posted by adam-bien.com on February 25, 2011 at 12:22 PM CET #

@Adam,

the beauty of the Instrumentation solution is that you don't need to know the details of how the VM stores things in terms of size. In particular the difference between 32bit and 64bit VMs becomes transparent.

Another advantage is that you can determine the size of a specific Session, for example, over its lifetime.

What starts as a tool for testing becomes a tool that can be used for monitoring applications even on Production systems where users are most adept at finding ways to screw things up.

Posted by Andy B on February 25, 2011 at 12:34 PM CET #

@Andy,

exactly. You should generate load and monitor your system. It is easier, faster and more fun than any premature optimizations.

thanks!,

adam

Posted by adam-bien.com on February 25, 2011 at 12:52 PM CET #

Well, if you initialize the List with new ArrayList(50000) you will get the result even faster, because the internal Array of the ArrayList does not have to be resized every time it gets too small...

Posted by Michael on March 09, 2011 at 09:02 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
realworldpatterns.com
...the last 150 posts
...the last 10 comments
License