Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6389107

(ref) Request Reference.link(Object,Object) for "biweak" caches

    Details

    • Type: Enhancement
    • Status: Open
    • Priority: P3
    • Resolution: Unresolved
    • Affects Version/s: 5.0
    • Fix Version/s: None
    • Component/s: core-libs
    • Labels:

      Description

      First off - this RFE will probably be closed as "won't fix", but I figure it's worth a shot.

      In various caching algorithms it is desirable to have a value which refers to the key. The clearest example is BeanInfo. A BeanInfo object has to keep a strong reference to the Class which it introspects, since it produces Method objects and so on. How do you then implement Introspector.getBeanInfo(Class)? You have some choices, none of which are satisfactory:

      1. Compute a fresh BeanInfo every time it is called. Can be expensive if it is called a lot. You want to cache it.

      2. Keep a WeakHashMap<Class,BeanInfo> cache. This is unacceptable since the Class object will never be collected (since the value refers to the key), and so dynamic class loaders will never be collected, JARs will never be unlocked, etc.

      3. Keep a WeakHashMap<Class,WeakReference<BeanInfo>>. This solves the problem in #2 but is not much of an improvement over #1 since the WeakReference<BeanInfo> will in practice be collected very quickly and have to be recomputed.

      4. Keep a WeakHashMap<Class,SoftReference<BeanInfo>>. This avoids frequent recomputation (does not prevent it altogether), and it does permit the Class to be collected; but it will keep the Class (and its ClassLoader, etc.) in memory much longer than it is actually needed, which is undesirable.

      5. Use #2 but introduce an explicit clearCache() method. This is undesirable as it forces every application making a custom class loader to know about this cache, even if its usage is buried in some otherwise self-contained library. And it forces the application to explicitly manage memory by deciding when the ClassLoader is no longer in use, which is not always possible.

      6. Use #3 but have the cache use as a key a subclass of the nominal key which includes a strong reference to the cache value. Works fine if you can in fact subclass the key's class and there is a reliable way of finding the cache key from an externally visible key. Does not work for java.lang.Class.

      7. Make the value hold only a weak (or soft) reference to the key. If some method is called on it which would require the original key, but the key has already been collected, throw an IllegalStateException or similar and ask clients to deal with that.

      Here are some instances of this problem in the real world:

      1. JDK #5102804 regarding BeanInfo (as above), with an impact on the NetBeans IDE (http://www.netbeans.org/nonav/issues/show_bug.cgi?id=46532) and only worked around in a crude way by #5.

      2. Apache Ant uses a similar per-Class cache:

      http://issues.apache.org/bugzilla/show_bug.cgi?id=30162#c5

      which suffers from the same problem, worked around using technique #3.

      3. Tomcat apparently has a similar problem:

      http://opensource2.atlassian.com/confluence/spring/pages/viewpage.action?pageId=2669#Memoryleak-classloaderwon&#39;tletgo-IntrospectionUtils

      Not known to be worked around yet.

      4. In the NetBeans IDE, we had a cache which was leaking memory.

      http://www.netbeans.org/nonav/issues/show_bug.cgi?id=48731

      Worked around using technique #7.
      JSR 292 turns up another interesting scenario. It describes a MethodHandle, which must hold strong refs to various Class objects (return and parameter types). The JSR requests that MethodHandle's be interned, i.e. the factory return the same object when given the same Class arguments.

      That would seem to be impossible without an RFE like this one. In fact just

        void link(Object from, Object to)

      would not seem to suffice: there is no single Class which is "distinguished" in general, since the classes might have different loaders, and not all of these loaders need be in a straight chain of ancestry.

      (If they were, then you could associate the handle with a class from the most derived loader: classes in base loaders could not be collected before this one anyway. For example, in 'String myMethod(MyObject x)', you would want to tie the MethodType to the loader of MyObject.class, not String.class. The problem can be seen with a method 'void m(A a, B b)' where A.class and B.class come from sibling loaders; linking the MethodType to, say, A.class would mean that B.class and thus its loader could not be collected so long as A.class and its loader existed, creating a potentially huge memory leak.)

      A more powerful primitive API would be

        void link(Object to, Object... from)

      with the semantics that 'to' is to be considered strongly held so long as all of the 'from' objects are strongly held, but collectible as soon as any of them becomes collectible.

      In the absence of such a facility, 292 will likely resort to a slightly compromised implementation of its spec: hold MethodType's using soft references. One of these references may be cleared during a GC cycle even when all the associated Class objects are strongly held, which is undesirable but not likely to be a significant performance problem under the assumption that active code in the JVM is holding local references to most of the MethodType's. (The impl can however assure that no two MethodType's with the same signature are live at a given time.)

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                mr Mark Reinhold
                Reporter:
                jglick Jesse Glick (Inactive)
              • Votes:
                0 Vote for this issue
                Watchers:
                4 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Imported:
                  Indexed: