Hello,
we have a quite huge application, using Vaadin for a rich UI. We rely heavily on Vaadins BeanItems and MethodPropertys.
Yesterday, I did a memory profiling, with, well, interesting results.
In total, the server uses 7,8GB of Heap Space. 1555MB of that are the real data from our HIbernate data model, then there are chaches, etc.
The interesting thing here is that for enriching that data model with vaadins items and properties, we use another 2,5GB of Heap space. The reasons for that are:
Each property has support for setArgs and getArgs - we never use this feature, but instead of null-initilising the fields, the constructor creates empty object arrays. For each fo our 12 million properties. This means we’ve got
450 MB of unused, empty Object[] in our Heap.
Isn’t that great?
Then, each property holds references to these Object arrays, to the Method’s used for getting and setting, to the object instance and an integer for that setArgument-Feature. For 12 million properties on a 64bit-VM, this equals to 740MB of memory just for the MethodProperty objects.
The BeanItems aren’t really better: They hold a LinkedList to keep the order of which you added properties to an Item. We never care in which order properties were added to an item, but we’ve at least 100MB of LinkedLists in our Heap for that feature for which i can’t see any usage.
And each BeanItems holds an (eagerly initialized) map of all Properties belonging to that Item, so we’ve got another 700MB here.
In total, that’s 1950MB for a GUI Layer on top of a 1555MB data model. That’s definitly too much, and we’d like to increase our usage of properties even more in the future.
So we propose the following solution:
- Make the setArgument-feature optional - if you want it, use another subclass of MethodProperty, or, if you think of it as core feature, provide a class which implements everything in MethodProperty BUT setArguments, saving us from the cost of two references, one integer and two empty Object arrays in each property.
- Even for the class with the setArgument-feature, don’t initialize the setArgs and getArgs with an empty array but null to indicate that no extra arguments are provided. The extra logic in the code is well justified by the easy memory savings availably
- To allow extending and enriching Vaadin MethodPropertys, provide protected getters and setters for internal variables like instance. Also think about removing the private final reference to bean in BeanItem, so subclasses can update the bean of the item whenever they want to - we’d like to do this in case the underlying hibernate-managed bean changed.
- To get rid of the duplicates of setMethod and getMethod and the property-maps in the items, the proposed solution is a bit more complicated:
PropertysetItem.getItemProperty works the following way:
Each Item class computes once the list of properties it has, saving these as PropertyDefs to a TLS. A PropertyDef contains the setMethod, the getMethod and maybe the name of the Property.
When getItemProperty is invoked, the item looks in it’s WeakHashMap of properties, trying to find the matching property - if there’s a property in it, return it. If there is no property in it, get the HashMap of PropertyDefs from the TLS, lookup the corresponding PropertyDef for the requested Property and construct a lean MethodProperty, just consisting of a reference to the instance of the Item, and a reference to the PropertyDef. This MethodProperty is then saved into the item-specific WeakHashMap and then returned to the caller.
This allows for very lean items: We only need a WeakHashMap for the properties which are currently in use and one reference to a TLS storage, which contains the PropertyDefs.
We also have very lean properties: We only need two references for a property + shared PropertyDefs. When using the property, it get’s its getMethod and setMethod from the
PropertyDef.
The reason for using TLS here and not a global ConcurrentHashMap is just lock contention - we construct so many items and properties, we can’t allow a lock to come in that critical code path which would problably reduce parallelism by a large amount.
This solution is just theoretical, we haven’t gone through implementing it (yet).
There are two options for us to go forward:
Implement our proposal locally, replacing every instance of BeanItem and MethodProperty with our own solution, probably having problems with the upgrade to Vaadin 7, maintainig even more code, replacing a piece of core code of a framework we use, being not a nice citizen in the OS world, etc.
As you can imagine, we’d not be happy about pursuing that path.
The other option is, obviously, trying to get our solution, or an entire different solution, which nullifies our memory problems pushed into Vaadin Core. Then we are happy, other users will profit from the reduced memory footprint as well, the upgrade path is ensured by the core team, etc.
Because if this, we’d like to hear your feedback - is the outlined solution possible? Where are the faults in our design? What are blockers for implementing this in Vaadin Core? Which version could it go in? While we plan to upgrade to Vaadin 7.0, fixing memory leaks is of higher priority than upgrading to Vaadin 7.0, and waiting for Vaadin 7.1 is definitly not possible, as that would mean we’d run memory efficient code (at least) until March or April - we have a growing user base, we don’t want to throw the memory out of the window.
If you did not understand parts or everything of what I said, don’t hesitate to ask, i’m happy to answer your questions and explain our problems to you.
Thanks for your support, thoughts, etc.
Cornelius