Re: JDOM and memory

Joe Bowbeer <[email protected]>
Newsgroups gmane.comp.java.jdom.general
Message-ID <CAHzJPEqHSF0CQXLL6QFhNrF+JG_wQHfgMa-sMK=tK7kLkHddGA@mail.gmail.com>
A per-document string pool is a feature of binary xml formats.

A potential problem with per-factory string pooling is the possibility of
retaining large character arrays.  Android's String class description
explains the problem:

This class is implemented using a char[]. The length of the array may
> exceed the length of the string. For example, the string "Hello" may be
> backed by the array ['H', 'e', 'l', 'l', 'o', 'W'. 'o', 'r', 'l', 'd'] with
> offset 0 and length 5.
> Multiple strings can share the same char[] because strings are immutable.
> The substring(int)<http://developer.android.com/reference/java/lang/String.html#substring(int)>
>  method *always* returns a string that shares the backing array of its
> source string. Generally this is an optimization: fewer character arrays
> need to be allocated, and less copying is necessary. But this can also lead
> to unwanted heap retention. Taking a short substring of long string means
> that the long shared char[] won't be garbage until both strings are
> garbage. This typically happens when parsing small substrings out of a
> large input. To avoid this where necessary, call new
> String(longString.subString(...)). The string copy constructor always
> ensures that the backing array is no larger than necessary.


...from http://developer.android.com/reference/java/lang/String.html

If xml parsers create new strings, is it to avoid retaining the entire
source document?

I suggest choosing a name for the Slim factory that is more descriptive of
what it does, as "slim" may depend on taste and application.

Joe

On Sat, Jan 28, 2012 at 8:38 AM, Rolf Lear wrote:

> Hi All ... An update...
>
> I have played with a number of options, and have not had significant
> success with any.
>
> Merging Content-list in to Element has a number of problems:
> 1. Document and Element end up duplicating a lot of code
> 2. It changes the API of Document and Element with it implementing
> List<Content>
>
> Document and Element almost always contain content... it is seldom that
> you have empty Elements (there is normally some text at least). As a
> result, the savings of not having to have a content array are limited.
>
> There can be some saving in not having a separate object as the list, but
> it does not amount to much. Given the issues with the API this approach
> does not make sense.
>
> Michael Kay suggested keeping the ContentList independent of the Element,
> and creating an instance when it was referenced in getContent(). The
> problem with this is that the management of ConcurrentModification becomes
> very complicated, and, as far as I can tell, essentially impossible if
> there are multiple differet instances of the ContentList class for any
> particular Element. Given that almost all Element instances have content,
> it is not worth the effort to lose the ConcurrentModification control, and
> not actually save any memory in a typical use case.
>
> So, neither option for changing the ContentList system is very successful.
>
> On the other hand, it is relatively common to have no Attributes on an
> Element, and some careful changes to the Element class (adding a
> hasAttributes() method and making the AttributeList variable a 'lazy'
> initialised field) this means that in ideal cases we never need to actually
> create an AttributeList instance for the Element. This has a significant
> impact on the 'hamlet' test, where there are essentially no attributes. It
> has no 'negative' impact on memory in the worst case either, and it has
> positive (small but significant) impact on performance.
>
> So, the lazy initialization of AttributeList is a 'win'.
>
> Finally, I have in the past had some success with the concept of 'reusing'
> String values. XML Parsers (like SAX, etc.) typically create a new String
> instance for all the variables they pass. For example, the Element names,
> prefixes, etc. are all new instances of String. Thus, if you have hundreds
> of Elements called 'car' in your input XML, you will get hundreds of
> different String Element names with the value 'car'. I have built a class
> that does something similar to String.intern() in order to rationalize the
> hundreds of different-but-equals() values that are passed in by the parsers.
>
> I have incorporated this 'caching' class in to a new JDOMFactory called
> 'SlimJDOMFactory'. This factory 'normalizes' all String values to a single
> instance of each unique String value. This significantly reduces the amount
> of memory used in the JDOM tree especially if there are lots of: similarly
> named attributes, elements, white-space-padding in otherwise empty
> elements, or between elements. This process is significantly slower
> through...
>
> For example, with the 'hamlet' test case, the 'baseline' memory footprint
> for hamlet in JDOM is 2.27MB in 4.75ms.
> With the SlimJDOMFactory it is: 1.77MB in 8.5ms
> With Lazy AttributeList it is: 2.06MB in 4.55ms
> With the both it is 1.57MB in 8.3ms
>
> I am pushing both of these changes in to github. The AttributeList is an
> easy one to justify. It is fully compatible with prior code, it has
> positive memory and perfomance impacts.
>
> The SlimJDOMFactory is also justifiable when you consider:
> 1. the user has to decide to use it specifically.
> 2. The memory saving can be very significant.
> 3. Even though the parse time is slower, the GC time savings can be
> significant if the document 'hangs around' for a long time - the quicker GC
> time can add up fast.
> 4. When you have lots of code doing comparisons it is much faster to do
> equals() calls on Strings that are == as well. It saves a hashCode
> calculation as well as a string character scan to prove equals().
>
> Rolf
>
>
> On 02/01/2012 3:27 PM, Rolf wrote:
>
>> Hi all.
>>
>> Memory optimization has never been a top priority for JDOM. At the same
>> time, for what it does, JDOM is not a 'terrible' memory user. Still, I
>> have done some analysis, and, I believe I can trim about a quarter to a
>> half of 'JDOM Overhead' memory usage by making two 'simple' changes....
>>
>> The first is to merge the ContentList class in to the Element class (and
>> also in to Document). This will reduce the number of Java objects by
>> about half, and that will save about 32 bytes per Element at a minimum
>> in a 64-bit JRE. Additionally, by lazy-initialization of the Content
>> array, we can save memory on otherwise 'empty' Elements.
>>
>> This can be done by extending the Element (and perhaps Document) class
>> to extend 'List'. It can all be done in a 'backward compatible' way, but
>> also leads to some interesting possibilities, like:
>>
>> for (Content c : element) {
>> ... do something
>> }
>>
>> (for backward compatibility, Element.getContent() will return 'this').
>>
>>
>> The second change is to make the AttributeList instance in Element a
>> lazy-initialization. This would save memory on all Elements that have no
>> attributes, but would have an impact for people who sub-class the
>> Element class and may expect the attributes field to be non-null.
>>
>>
>> I am trying to get a feel for how important this sort of optimization
>> may be. If there is interest then I will make some changes, and test the
>> impact. I may make a separate branch in github to test it out....
>>
>> If the above changes are unrealistic then I don't think it makes sense
>> to even try....
>>
>> Rolf
>>
>

_______________________________________________
To control your jdom-interest membership:
http://www.jdom.org/mailman/options/jdom-interest/[email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.