Skip to Content

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

To report this post you need to login first.

3 Comments

You must be Logged on to comment or reply to a post.

  1. Valery Silaev
    Markus,

    Ok, you have calculated size of types involved. What’s next? What is an alternative to dynamic properties pattern? Overall, is it (alternative) necessary?

    VS

    (0) 
    1. Markus Kohler Post author
      Hi,
      The first alernative is to avoid the pattern in the first place. The overuse of this pattern is often an indicator for unclear requirements.

      I also pointed out that a common mistake is to not share the keys.

      Actually the way you would optimize it, depends on very much what your requirements are.
      There is no single way. 
      I will try to show some alternatives in the next blog.
      Regards,
      Markus

      (0) 
    2. Richard Birenheide
      Reading through Fowler’s article I would refrain using dynamic proerties at all, when providing API. Many of the problems posed there could simply be solved with inheritance. java.net.Authenticator shows nicely how that can work. One can extend the class and put back an instance to the API. Since the API anyway can only know about declared properties (all others are only known to the client API), these are handled appropriately. The only disadvantage in that particular example is that there is only a static method for providing the implementation back. Of course this will not work for all possible scenarios but I think for most of them. Especially Fowler’s examples could be implemented nicer that way.

      Regards
      Richard

      (0) 

Leave a Reply