13 Replies Latest reply on May 22, 2008 10:52 AM by brian.stansberry

    Composite Map key and weak reference

    alesj

      How to solve this issue:

      I have a Class ref and array of Annotation refs, which together compose my Map key.
      Since I have no callback hooks for removing this key I need something similar as what WeakHashMap would do if my key was only that Class ref - removing the whole entry when Class ref is GCed.

      Is there an easier way than rewriting the whole WeakHM (or some similar construct)?

        • 1. Re: Composite Map key and weak reference
          jason.greene

          So I take it the annotation refs point to annotations on the class ref?
          I could see a couple of options:

          1. You could use a WeakIdentityHM using a Class ref as the key, and a nested normal HM using a key which has an array of weak annotation refs. This would then be wrapped in a delegate which emulates the composite key behavior.

          2. Use a normal map, of any kind, and extend WeakReference to have an additional strong reference field, that points to your composite key class. Use this special reference for both the Class ref and all of the annotation refs. These are then registered in a ref queue, which is processed to remove gc'd entries periodically when accessing the map (by using the key reference). For this to work the key must do an identity check before comparing its contents (since the contents are gone during cleanup). If the map is concurrent you could do this cleanup in a separate thread.



          • 2. Re: Composite Map key and weak reference
            brian.stansberry

            I believe Jason's #2 is what I was thinking about in our chat discussion. A rough impl:

            package hacks;
            
            import java.lang.annotation.Annotation;
            import java.lang.ref.Reference;
            import java.lang.ref.ReferenceQueue;
            import java.lang.ref.WeakReference;
            
            public class Key
            {
             private static final ReferenceQueue refQueue = new ReferenceQueue();
            
             private final KeyRef keyRef;
             private final WeakReference<Annotation>[] annotations;
            
             public Key(Class clazz, Annotation[] annotations)
             {
             this.keyRef = new KeyRef(this, clazz, refQueue);
             this.annotations = new WeakReference[annotations.length];
             for (int j = 0; j < annotations.length; j++)
             {
             this.annotations[j]= new WeakReference<Annotation>(annotations[j]);
             }
             }
            
             @Override
             public boolean equals(Object obj)
             {
             if (obj instanceof Key)
             {
             Key other = (Key) obj;
             Class clazz = keyRef.get();
             Class otherClass = other.keyRef.get();
             if (clazz == null || otherClass == null)
             {
             cleanKeyRefs();
             return false;
             }
             else
             {
             if (shouldCleanKeyRefs())
             cleanKeyRefs();
             return clazz.equals(otherClass); // need to add in annotations
             }
             }
            
             return false;
             }
            
             /** Allows optimization; i.e. change to only poll the queue every X invocations */
             private boolean shouldCleanKeyRefs()
             {
             return true;
             }
            
             /**
             * Poll the reference queue and clear the strong ref to Key from all
             * reference objects. This allows the Key to get removed from
             * WeakHashMap.
             */
             private void cleanKeyRefs()
             {
             Reference ref = null;
             while ((ref = refQueue.poll()) != null)
             {
             ((KeyRef) ref).key = null;
             }
             }
            
             private static class KeyRef extends WeakReference<Class>
             {
             private Key key;
            
             KeyRef(Key key, Class clazz, ReferenceQueue queue)
             {
             super(clazz, queue);
             this.key = key;
             }
             }
            }


            • 3. Re: Composite Map key and weak reference
              brian.stansberry

              Oh, but my class above is intended to be passed as a key to WeakHashMap. The ReferenceQueue polling will remove the only strong ref to the Key, at which point the WeakHashMap will do its normal thing and make the Key/value pair eligible for removal.

              • 4. Re: Composite Map key and weak reference
                alesj

                 

                "jason.greene@jboss.com" wrote:
                So I take it the annotation refs point to annotations on the class ref?

                Not really.
                But some of class's annotations might be equal to that annotations.

                I'm using this with WebBeans kind of matching:
                @Red
                @Blue
                @Inject
                public void setFoo(Foo foo)
                

                where class might have
                @Red @Blue @Green
                public class RGBFoo implement Foo
                


                The code is here - but the caching is currently wrong :-)
                -
                http://anonsvn.jboss.org/repos/jbossas/projects/microcontainer/trunk/kernel/src/main/org/jboss/kernel/plugins/annotations/wb/WBInjectionResolver.java

                • 5. Re: Composite Map key and weak reference
                  jason.greene

                   

                  "alesj" wrote:
                  "jason.greene@jboss.com" wrote:
                  So I take it the annotation refs point to annotations on the class ref?

                  Not really.
                  But some of class's annotations might be equal to that annotations.


                  Ah, ok, then you don't want the first option.


                  • 6. Re: Composite Map key and weak reference
                    jason.greene

                     

                    "bstansberry@jboss.com" wrote:
                    Oh, but my class above is intended to be passed as a key to WeakHashMap. The ReferenceQueue polling will remove the only strong ref to the Key, at which point the WeakHashMap will do its normal thing and make the Key/value pair eligible for removal.


                    IMO it would be better to connect the logic to the map, then the entry is cleared in one pass, and you aren't tied to a particular map implementation.

                    • 7. Re: Composite Map key and weak reference

                      I don't really understand.

                      If the class references the annotations then all you need to do is make sure
                      you have something like:

                      WeakHashMap<Class<?>, WeakValueHashMap<List<Annotation>, Whatever>>
                      


                      You then need to do a double lookup

                      Pseudo code (need to check nulls):
                      Whatever w = map.get(clazz).get(annotations);
                      


                      This will basically remove the reference when either the initial class or the
                      "Whatever" instance is GCed.

                      It don't think it really matters if you have hard refernces on the Annotations classes
                      in the List since that will also be true while the original Class/ClassLoader or
                      Whatever holds references?

                      Of course if you really want to be safe, you could try to create a
                      WeakCompositeKeyHashMap
                      which removes the entry when any of the elements in the composite key
                      gets GCed.

                      • 8. Re: Composite Map key and weak reference
                        alesj

                         

                        "adrian@jboss.org" wrote:

                        If the class references the annotations then all you need to do is make sure you have something like:

                        No, it doesn't.
                        See my response to Jason.

                        "adrian@jboss.org" wrote:

                        Of course if you really want to be safe, you could try to create a
                        WeakCompositeKeyHashMap
                        which removes the entry when any of the elements in the composite key
                        gets GCed.

                        Yeah, that I could figure. :-)
                        See my initial post:
                        "alesj" wrote:

                        Is there an easier way than rewriting the whole WeakHM (or some similar construct)?


                        • 9. Re: Composite Map key and weak reference
                          alesj

                           

                          "bstansberry@jboss.com" wrote:
                          Oh, but my class above is intended to be passed as a key to WeakHashMap. The ReferenceQueue polling will remove the only strong ref to the Key, at which point the WeakHashMap will do its normal thing and make the Key/value pair eligible for removal.

                          Hmmm ... trying to put your code into real life showed some issues.

                          The way you do your refQueue is wrong. :-)
                          And I don't see how you can do it right.

                          Since what you have to pass into WeakReference as Queue instance is the initial queue of actual type of your WeakReference - the type that's gonna be GCed - in our case the Class ref.

                          But in that case we cannot do this
                           private void cleanKeyRefs()
                           {
                           Reference ref = null;
                           while ((ref = refQueue.poll()) != null)
                           {
                           ((KeyRef) ref).key = null;
                           }
                           }
                          


                          I'll try to do WeakCompositeKeyMap, doing something similar as what we do in WeakValueHashMap.

                          • 10. Re: Composite Map key and weak reference
                            jason.greene

                            Ales did you read the second suggestion I posted.

                            • 11. Re: Composite Map key and weak reference
                              alesj

                               

                              "jason.greene@jboss.com" wrote:
                              Ales did you read the second suggestion I posted.

                              Sure. ;-)
                              But I also think I'm wrong with my previous post, as Brian's impl should work as well.
                              If not, I'll impl it your (2nd) way.

                              • 12. Re: Composite Map key and weak reference
                                alesj

                                 

                                "bstansberry@jboss.com" wrote:

                                public class Key
                                {
                                 private static final ReferenceQueue refQueue = new ReferenceQueue();
                                
                                 private final KeyRef keyRef;
                                 private final WeakReference<Annotation>[] annotations;
                                
                                 public Key(Class clazz, Annotation[] annotations)
                                 {
                                 this.keyRef = new KeyRef(this, clazz, refQueue);
                                 this.annotations = new WeakReference[annotations.length];
                                 for (int j = 0; j < annotations.length; j++)
                                 {
                                 this.annotations[j]= new WeakReference<Annotation>(annotations[j]);
                                 }
                                 }
                                

                                Do I really need these annotations to be wrapped in weak refs?
                                Wouldn't they get immediately gced?

                                Or if I just leave them strong, my whole Key instance will be gc candidate the moment I nullify it in here
                                 while ((ref = refQueue.poll()) != null)
                                 {
                                 ((KeyRef) ref).key = null;
                                 }
                                

                                since nothing else now has a strongly ref on it.


                                • 13. Re: Composite Map key and weak reference
                                  brian.stansberry

                                  I just wrapped them in WeakRefs in case they would leak the Class' classloader, preventing gc of the Class. Don't know whether in your scenario they will or not.

                                  TBH, I didn't think much at all about the annotations; I was focused on the Class; just threw in the annotation array because you said your key had one.