> Is the number of JRealClassType instances just going up and up each refresh?

Yep. I didn't explicitly check until you asked, but comparing heap diffs
before/after multiple refreshes, each refresh was adding ~1800 new
JRealClassTypes that never went away.

> Can you figure out who is holding references to them (referrants)?

Turns out I can. Thank you for the rather logical suggestion--I had
looked at the GC root paths and, once I saw AppClassLoader, had given up
thinking it was going to be an PITA classloader issue.

But, no, its just some static caches. It took a few tries, but the
attached patch "fixes" the leaks and I can now run my whole test suite
and DevMode stays snappy.

Note that I use the term "fixes" very loosely as:

* ResourceFactory.cache just needs a WeakHashMap, pretty easy

* OwnerFieldClass.FIELD_CLASSES needs a hash map that is weak on both
  keys and values. Or some way to explicitly clear the values when
  the TypeOracle is done with.

  I tried a WeakHashMap and it was not enough as the OwnerFieldClass
  value is what holds the JRealClassType reference. What did work, was a
  stupid, ugly hack of clearing the cache on each new oracle seen (which
  is a bad idea, it would break multiple clients use DevMode
  concurrently, but it proved the point).

* AbstractResourceContext.CACHES is already a WeakHashMap, however, the
  values end up being IdentityHashMaps with JRealClassTypes keys, which
  reference the oracle, so, contrary to the javadoc, it does actually
  keep TypeOracles from being GC'd.

  The same ugly hack from issue 2 also solved this final one.

As my hacks obviously show, I don't know of a good way to deal with the
2nd or 3rd issues. If you have a suggestion, I can try and run with it,
but I'd also be just fine with someone more knowledgeable about the
codebase finding an elegant solution. :-)

Thanks!

- Stephen

-- 
http://groups.google.com/group/Google-Web-Toolkit-Contributors
Index: user/src/com/google/gwt/i18n/rebind/ResourceFactory.java
===================================================================
--- user/src/com/google/gwt/i18n/rebind/ResourceFactory.java	(revision 8507)
+++ user/src/com/google/gwt/i18n/rebind/ResourceFactory.java	(working copy)
@@ -91,7 +92,7 @@
    */
   public static final char LOCALE_SEPARATOR = '_';
 
-  private static Map<ClassLocale, ResourceList> cache = new HashMap<ClassLocale, ResourceList>();
+  private static Map<ClassLocale, ResourceList> cache = new WeakHashMap<ClassLocale, ResourceList>();
   private static List<ResourceFactory> loaders = new ArrayList<ResourceFactory>();
 
   static {
Index: user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java
===================================================================
--- user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java	(revision 8507)
+++ user/src/com/google/gwt/uibinder/rebind/model/OwnerFieldClass.java	(working copy)
@@ -44,7 +46,8 @@
    * Global map of field classes. This serves as a cache so each class is only
    * processed once.
    */
-  private static final Map<JClassType, OwnerFieldClass> FIELD_CLASSES = new HashMap<JClassType, OwnerFieldClass>();
+  private static final Map<JClassType, OwnerFieldClass> FIELD_CLASSES = new WeakHashMap<JClassType, OwnerFieldClass>();
+  private static TypeOracle lastOracle;
 
   /**
    * Gets or creates the descriptor for the given field class.
@@ -55,6 +58,10 @@
    */
   public static OwnerFieldClass getFieldClass(JClassType forType,
       MortalLogger logger) throws UnableToCompleteException {
+    if (forType.getOracle() != lastOracle) {
+      FIELD_CLASSES.clear();
+      lastOracle = forType.getOracle();
+    }
     OwnerFieldClass clazz = FIELD_CLASSES.get(forType);
     if (clazz == null) {
       clazz = new OwnerFieldClass(forType, logger);
Index: user/src/com/google/gwt/resources/rebind/context/AbstractResourceContext.java
===================================================================
--- user/src/com/google/gwt/resources/rebind/context/AbstractResourceContext.java	(revision 8507)
+++ user/src/com/google/gwt/resources/rebind/context/AbstractResourceContext.java	(working copy)
@@ -46,6 +46,7 @@
    * map and will not prevent TypeOracles from being gc'ed.
    */
   private static final Map<TypeOracle, Map<String, Object>> CACHES = new WeakHashMap<TypeOracle, Map<String, Object>>();
+  private static TypeOracle lastOracle;
 
   /**
    * The key we use to store the expected TypeOracle reload count.
@@ -60,6 +61,11 @@
   }
 
   private static Map<String, Object> getCache(TypeOracle oracle) {
+    if (oracle != lastOracle) {
+      CACHES.clear();
+      lastOracle = oracle;
+    }
+
     Map<String, Object> toReturn = CACHES.get(oracle);
     if (toReturn != null) {
       long expectedCount = (Long) toReturn.get(TYPE_ORACLE_RELOAD_COUNT_KEY);

Reply via email to