...
Creating objects in Java is trivial. In fact, it can be described as being too easy to create objects. When a user logs into uPortal for the first time, over 40MB of objects are created (depending on channel content). The resulting number of objects is much lower than 40MB, but creating over 40MB of objects (most being temporary) in a few seconds is an expensive process. Though the Java Garbage Collector (GC) has made great strides in performance over the past few years, especially in regards to temporary object creation, reducing the large number of objects created can only increase overall application performance.
The following image shows a few hotspots captured from the uPortal 2.4.2 quickstart:
The following steps were performed to capture the data:
- start the server and attach YourKit profiler
- login once and exit
- have YourKit start collecting object counts
- login once and exit
- snapshot
- login once and exit
- snapshot
- compare snapshots
There is much data generated by YourKit, but the image shows the excessive number of objects created by two uPortal-code methods (addressed below). The interesting number is the 240k objects created to render the two pages (on login and logout). Of these, almost 110k were created by the two methods shown (approx 2MB out of 14.5MB).
isDebugEnabled()
Tracing is commonly found in application code. It will usually look like:
Code Block | ||||
---|---|---|---|---|
| ||||
for (Enumerator e = users.getEnumeration(); e.hasMoreElements(); ) { User user = (User) e.nextElement(); log.debug("user: " + user + ", account: " + accounts.get(user) + ", classes: " + classes.get(user)); // business logic... } |
...
A more GC-friendly approach is to wrap all log requests (not just debug) with a conditional:
Code Block | ||||
---|---|---|---|---|
| ||||
for (Enumerator e = users.getEnumeration(); e.hasMoreElements(); ) { User user = (User) e.nextElement(); if (log.isDebugEnabled()) { log.debug("user: " + user + ", account: " + accounts.get(user) + ", classes: " + classes.get(user)); } // business logic... |
...
Entities are generally used in XML (and HTML) to allow special characters to exist within an XML document. For example:
Code Block | ||||
---|---|---|---|---|
| ||||
<html> <body> This is my XML document: &lt;xmldoc&gt; &lt;data/&gt; &lt;/xmldoc&gt; </body> </html> |
If "<xmldoc>" had been placed in the body of the document, it would have caused a parsing exception since "<xmldoc>" is not a valid HTML element. The "<<" and corresponding ">>" are necessary for successful parsing.
...
When loading the file, all entities are stored in a HashMap keyed on an Integer object of the entity value. So, map.get(new Integer(160))
will return a String object value "nbsp" and when writing the character, would be replaced by " " (done by org.jasig.portal.serialize.BaseMarkupSerializer.printEscaped(int)
.
...
Our solution was to replace the Integer object keyed map with a String[], indexed by the entity value. The array grows dynamically on initialization to accommodate existing (and new) entity values. Though sparsely populated, it is the a faster implementation for resolving an entity String value since now it is just an index into an array. Now No new Java objects are created during normal processing of entity replacement.
...