question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Retrieving the GWT SDK version is incredibly slow in large projects

See original GitHub issue

Background: We have a very large code base that consists of more than 150 sub projects, with roughly 70% of them being either directly or transitively referenced by a distribution project that ties everything together. The whole code base is a Gradle project, and we use the Buildship plugin to import everything into Eclipse. We also have some Gradle build scripts to set the correct GWT settings (enabling the GWT nature on the distribution project, assigning the SDK and configuring other project-specific options) before the import starts.

Problems: We noticed that having the GWT nature enabled on the project has a severe negative impact on the performance of Eclipse in general:

  • Without the GWT nature, the import takes roughly 15 minutes (on an SSD). With it, that time almost doubles.
  • Validating the GWT project with the GWTProjectValidator implementation has a 4-5 minute wind-up time before it even starts doing work. Since this validation is performed during the import and also before any launch (either the application itself, a Gradle task or a unit test), we end up with a lot of idle waiting time.
  • The UI menu for launch configurations freezes completely and refuses to subsequently open after switching to the overview for the GWT launch configuration.
  • Eclipse is often waiting a long time for background work to complete, which delays many user actions like saving of a file and rebuilding the workspace.

Cause: Since this is pretty much a show stopper that makes Eclipse nearly unusable for us, we started investigating the cause and found it in ProjectBoundSdk.createClassLoader() (in GwtSdk.java), which is called when the getVersion() method is used to retrieve a string for the SDK’s version number. This apparently searches the entire classpath of the project and all its referenced subprojects for a specific class in order to call a method on it via reflection.

To be even more precise, the call to JavaRuntime.computeDefaultRuntimeClassPath(javaProject) in that method is what’s taking several minutes to return. This call causes Buildship to resolve its classpath container, which apparently involves many calls to File.getCanonicalPath() (aka slow native file system operations). I don’t know if this is something that’s specific to Gradle Buildship or if it is done the same way in other project types.

getVersion() is called in many places:

  • The GWTProjectValidator.
  • The LaunchConfigurationUpdater calls this several times, which in turn gets activated whenever the LaunchConfigAffectingChangesListener detects changes to the classpath. (This also runs during the initial project import.)
  • equals() and hashCode() implementation of AbstractSdk. All of these are likely responsible for the problems mentioned above.

createClassLoader() apparently also has been identified as performance critical in the past, as the TODO comments there indicate:

/**
 * Returns a {@link ClassLoader} that is backed by the project's runtime classpath.
 *
 * TODO: This returns a classloader which contains ALL of the jars of the project. Lookups on
 * this thing are going to be SLOW. Can we optimize this? We could create a classloader that
 * just contains the jars that GWT requires. Maybe caching is the right solution here.
 *
 * TODO: Why can't we just delegate to {@link #getClasspathEntries()} when generating the
 * classloader URLs? Why do we have to add every URL that is part of the project? That would
 * certainly speed up lookups on this classloader. Maybe we cannot do this because project-bound
 * sdks handle the case of source-based runtimes, and in that case, we need all of the
 * dependencies as part of the classloader.
 */

Workaround: For our own needs, we added a workaround where we simply read the SDK version string from the plugin’s project preferences, which we write there ourselves (via the Gradle build script). If it’s missing, we fall back to whole classpath lookup thing, but that case shouldn’t ever happen again for us if the project is set up correctly. We could confirm that this change solves the perceived problems.

Whether such a hack is suited for the “official” release is up to you to decide. Perhaps you can think of a more elegant solution as well. I am aware that this is probably not as easy to reproduce, since it requires quite a large project.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:17 (11 by maintainers)

github_iconTop GitHub Comments

1reaction
branflake2267commented, Aug 14, 2018

Sounds good. I have two long weekends in a row, so it might take me 3 weeks to get back to this. Although I have some dedicated time road mapped and coming to work on this plugin coming. I’m getting closer to that time.

1reaction
CrushaKRoolcommented, Aug 13, 2018

Your solution is indeed better for the average user, since it does not involve any manual work (and is also not just a dirty hack like mine).

It is not as optimal for us, however, since the properties key that stores cached version depends strongly on the installation directory and SDK location of the end user. (It looks like this in my case: gwtVersion_E:\\Dev\\eclipse_photon\\plugins\\com.gwtplugins.gwt.eclipse.sdkbundle.gwt27_2.7.0.201808101238\\gwt-2.7.0) This makes it rather difficult to set this property in advance via a Gradle plugin, since that plugin won’t know about the installation path (and has no reason to, especially if it runs in an environment without Eclipse). And without setting it in advance, we are still giving away 4-5 minutes that could be saved otherwise. I am not asking you to do anything about that. If it really bothers us too much, we’ll just have to keep using our forked fix. Just wanted to point it out.

(Also, not sure if the GWT version is ever retrieved by more than one thread, in which case it could start multiple classpath resolves before finally having the version in its cache.)

Read more comments on GitHub >

github_iconTop Results From Across the Web

GWT is slow in Development Mode - Stack Overflow
I had around 20 break point in my GWT code. Running in debug mode in Eclipse was VERY VERY slow. I removed the...
Read more >
Building a GWT project in Eclipse 2019-03+ with Java 11.
Starting the codeserver is actually slower than the app (at least for us, and the app is huge). When you debug the app,...
Read more >
GWT Release Notes
GWT Designer doesn't work with 2.7 and is no longer supported. (Source code is available if someone wishes to revive this project.) IFrameLinker...
Read more >
google-web-toolkit
ID Status Summary 5856 AssumedStale Tab Order of TextInputCell 5853 Invalid getSerializationSignature method signature changed 5848 Invalid TextField cannot convert string to integer
Read more >
Reify for Developers - SmartClient
You can also use multiple Reify projects as part of a single, larger application ... If you have an existing Smart GWT application,...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found