Memory usage, object pools and String.intern()

Memory usage is a critical point in multi agent simulations, just because there usually are so many of them. Thus, every single byte that is used to describe a single agent enlarges the memory consumption of the whole system massively.
Our agents have several attributes (like sex, car-availability, employment status), their plans contain activities with a type and legs with a mode. Each of these attributes are stored as Strings. Considering that even an empty String takes up to 40 bytes in Java and characters in Java are always 16bit, even a small string like "f' (for Person.sex) or "car" (for Leg.mode) takes a lot of memory—and that's for every single agent, leg and activity!
It is quite obvious that it does not make sense to have more than one string with the same content multiple times, especially when every instance uses so much memory. This is where object pools are often used: a so called pool stores commonly used objects exactly once, and other objects can refer to these objects instead of holding their own, identical, instances. In our case this means that instead of having separate instances of String for every "f", "car" or other attribute, each occurring value exists only once as a String-object, and all the agents, legs and activities only reference the corresponding pool object instead of storing their own instance. Having a population of 200k agents with 5 attributes would contain 1mio strings—that would be more than 40MB of RAM alone, not yet counting the memory used by plans, leg-modes, activity-types. But when using our "object pool" we only need about 10 or 15 different Strings (depending on the number of different values in the attributes), instead of 1mio!
The class String offers already such an internal object pool, so that this optimization can be used without much additional code:

String.intern();

The documentation to String.intern() reads as follows:

Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

So, the setters for such string-attributes can now be written like the following one:

class Act {
  String type = null;
  ...
  public void setType(String type) {
    this.type = type.intern();
  }
}

Using this simple trick, the memory consumption was reduced by over 25%  when reading in 160k agents, while the time used for reading the agents did not change significantly (50secs vs 49secs). Reading larger populations should result in even larger memory savings, as most likely no additional strings have to be added to the pool, and all attributes can just reference to them.