Currently Being Moderated

Hi all,

This time I will talk about a very popular pattern that can led to a big increase in memory consumption. You will learn also the rules for computing how much memory a Java objects needs.

The Pattern is called "Dynamic Properties" and is described here by Martin Fowler.

The key thing about fixed properties is that you fix them at design time, and all instances at run time must follow that decision. For some problems this is an awkward restriction. Imagine we are building a sophisticated contact system. There are some things that are fixed: home address, home and work phone, email. But they’re all sorts of little variations. For someone you need to record their parent’s address, another has a day work and evening work numbers. It’s hard to predict all these things in advance, and each time you change the system you have to go through compiling, testing, and distribution. To deal with this you need to use dynamic properties.

fixed properties are essential the same as Java Beans.
A popular variant of the "Dynamic Properties" are "Flexible Dynamic Properties"

Provide an attribute parameterized with a string. To declare a property just use the string.

So let's check what the memory consumption overhead for implementing the "Flexible Dynamic Properties" pattern can be.

The general rules for computing the size of an object on the SUN/SAP VM are :

32 bit:

  • Arrays of boolean, byte, char, short, int: 2 * 4 (Object header) + 4 (length-field) + sizeof(primitiveType) * length -> align result up to a multiple of 8
  • Arrays of objects: : 2 * 4 (Object header) + 4 (length-field) + 4 * length -> align result up to a multiple of 8
  • Arrays of longs and doubles: : 2 * 4 (Object header) + 4 (length-field) + 4 (dead space due to alignment restrictions) + 8 * length
  • java.lang.Object: 2 * 4 (Object header)
  • other objects: sizeofSuperClass + 8 * nrOfLongAndDoubleFields + 4 * nrOfIntFloatAndObjectFields + 2 * nrOfShortAndCharFields + 1 * nrOfByteAndBooleanFields -> align result up to a multiple of 8

64 bit:

  • Arrays of boolean, byte, char, short, int: 2 * 8 (Object header) + 4 (length-field) + sizeof(primitiveType) * length -> align result up to a multiple of 8
  • Arrays of objects: : 2 * 8 (Object header) + 4 (length-field) + 4 (dead space due to alignment restrictions) + 8 * length
  • Arrays of longs and doubles: : 2 * 8 (Object header) + 4 (length-field) + 4 (dead space due to alignment restrictions) + 8 * length
  • java.lang.Object: 2 * 8 (Object header)
  • other objects: sizeofSuperClass + 8 * nrOfLongDoubleAndObjectFields + 4 + nrOfntAndFloatFields + 2 * nrOfShortAndCharFields + 1 * nrOfByteAndBooleanFields -> align result up to a multiple of 8

Note that an object might have unused space due to alignment at every inheritance level (e.g. imagine a class A with just a byte field and class B has A as it's superclass and declares a byte field itself -> 14 bytes 'wasted on 64 bit system).

Thanks to my colleague Ralf Sch. for letting me know these rules.

Assume a simple implementation of the "Flexible Dynamic Properties" pattern using a HashMap. Each Property is stored as a key value pair. We want to compute how much memory a simple object with nothing else than 4 properties needs.

An empty HashMap (on JDK 1.4 SUN Intel 32 bit) consumes 120 Bytes. Each HashMapEntry has 24 bytes overhead. So with 4 properties the object needs 8+120+4 * 24=224 bytes, when all keys and values are not counted. If you would use static Properties,the same would only cost 8+4 * 4=24 bytes.

Note that you almost always want, that at least the keys are shared. The simple reason is that usually the number of keys is small and independent of the number of objects that have properties. Keys of Hashmaps are not allowed to be changed anyway. In case you are using Strings changing the Strings is tricky, because Strings are immutable. So there's really no good reason to not share the Strings in this case.

You will not really scale, when you dont' share the keys, which can easily happen when you read the keys from the database and don't check whether you already have seen the key (using a Hashmap for example).

Duplicating strings not only wastes memory it may also make access to HashMaps slower. Take a look at the sources for String.equals() :

public boolean equals(Object anObject) { if (this == anObject) { return true; }
...

equals() first compares for identity ! So in case of the "Flexible Dynamic Properties", if you don't share the keys you also slow down access to your properties, because if the Strings to be compared are equal, but not identical ,the code in the JDK will compare the Strings character by character. This can be, depending on the length of the String, 5 or more times slower.

Regards,
Markus

Comments

Actions

Filter Blog

By date: