GWT RPC deserialization vs IE8

Almost every web developer knows the nasty browser popup asking to cancel a long running JavaScript execution. In the case of Internet Explorer this message is “A script on this page is causing Internet Explorer to run slowly”.

This post will show what’s the specific behaviour of IE8, how this affects GWT RPC’s deserialization mechanism and how you can avoid these problems.

About slow JavaScript on IE8

Most browsers wait a specific amount of time (e.g. 5 sec) until the before-mentioned message is shown. For Internet Explorer 8, this works a little bit different as the relevant criteria is the “number of executed script statements” and this is by default set to 5 million statements.

This is bad because on typical desktop hardware of 2014 it takes less than a second until this problem occurs. So it’s much more difficult to avoid this on IE8 as on other browsers.

But this is also good because of this behaviour, it’s deterministic and you can even reproduce this on your overpowered developer hardware.

GWT RPC and slow execution

In GWT, we have tools to avoid this kind of problem. The key to success is the Scheduler class that allows us to run code in a new JavaScript block (scheduleDeferred) so that the counter is being reset. Even incremental executions are possible using the Scheduler…

There are cases where you can’t simply defer some code to avoid these problems. One case is the deserialization code of GWT RPC. The following characteristics increase the number of statements needed for deserialization:

  • number of classes handled by the specific RPC
  • complexity of the data model
  • amount of data/objects transferred by one RPC call

Here are some general recommendations that will help to optimize the performance and efficiency of GWT RPC’s serialization mechanism:

  • Always use the most specific interface/class possible
    • Don’t ever use “Serializable” as parameter/return type or field in any object transferred by GWT RPC
    • e.g. use ArrayList instead of List in this specific case
  • Only transfer the data you really need (including IDs needed to load additional data)

Java Collections + GWT RPC = Much Fun

A specific category of problems is caused by using Java’s Collections API…

I recently had problems with slow JavaScript executions on deserialization where the data transferred was less than 100kB. This was caused by heavy usage of HashSets and HashMaps. It turned out that this caused several millions of JavaScript statements during deserialization.

Let’s have a look at the deserialization code of a Map to see what’s happening:

public static void deserialize(SerializationStreamReader streamReader, Map instance) throws SerializationException {
    int size = streamReader.readInt();

    for (int i = 0; i < size; ++i) {
        Object key = streamReader.readObject();
        Object value = streamReader.readObject();

        instance.put(key, value);
    }
}

So this code uses the regular “put” method to insert all transfered values. In the case of HashMap these aspects cause additional statements:

  • Many calls to “hashCode/equals” for the key objects
  • Expensive code to find the right place where to insert the entry to the map

This code gets more expensive the more entries you put in the map. This also applies to HashSets as they use the same mechanisms as HashMaps.

In the concrete case these Maps and Sets were used for optimized access to several objects. The solution for this was to use Lists (which are much cheper regarding deserialization) to transfer the data and mapping it on the client to the optimized structures.

A nice trick to hide this in the transfer objects is to use transient fields:

private List<MyType> data;
private transient Map<MyKey, MyValue> optimizedData;
public Map<MyKey, MyValue> getOptimizedData() {
    if(optimizedData == null) {
        optimizedData = optimizeIt();
    }
    return optimizedData;
}

This causes the “optimizedData” field to not be included in the serialization. On first access the optimized version of the data is initialized and cached for further usage.

Short URL for this post: http://wp.me/p4nxik-20N
This entry was posted in Java Web Frameworks and tagged , , , , , , . Bookmark the permalink.

Leave a Reply