Sorry, I was confused about who wrote what. Sounds like David and I are in agreement that you can remove the synchronization - I believe that would be much cleaner. And resolveClass does nothing and is final so no worries there.
thanks, Karen On Oct 29, 2014, at 2:37 AM, David Holmes wrote: > On 29/10/2014 4:04 PM, Ioi Lam wrote: >> >> On 10/28/14, 7:34 PM, David Holmes wrote: >>> Hi Karen, >>> >>> I haven't been tracking the details of this and am unclear on the >>> overall caching strategy however ... >>> >>> On 29/10/2014 8:49 AM, Karen Kinnear wrote: >>>> Ioi, >>>> >>>> Looks good! Thanks to all who have contributed! >>>> >>>> A couple of minor comments/questions: >>>> >>>> 1. jvm.h (hotspot and jdk) >>>> All three APIs talk about loader_type, but the code uses loader. >>>> >>>> 2. Launcher.java >>>> To the best of my understanding - the call to findLoadedClass does >>>> not require synchronizing on the class loader lock, >>>> that is needed to ensure find/define atomicity - so that we do not >>>> call defineClass twice on the same class - i.e. in >>>> loadClass - it is needed around the findLoadedClass / >>>> findClass(defineClass) calls. This call is just a SystemDictionary >>>> lookup >>>> and does not require the lock to be held. >>> >>> If the class can be defined dynamically - which it appears it can >>> (though I'm not sure what that means) - then you can have a race >>> between the thread doing the defining and the thread doing the >>> findLoadedClass. By doing findLoadedClass with the lock held you >>> enforce some serialization of the actions, but there is still a race. >>> So the only way the lock could matter is if user code could trigger >>> the second thread's lookup of the class after the lock has been taken >>> by the thread doing the dynamic definition - whether that is possible >>> depends on what this dynamic definition actually is. >>> >> I copied the code from ClassLoader.loadClass, which does it it a >> synchronized block: > > >> class ClassLoader { >> protected Class<?> loadClass(String name, boolean resolve) >> throws ClassNotFoundException >> { >> synchronized (getClassLoadingLock(name)) { >> // First, check if the class has already been loaded >> Class<?> c = findLoadedClass(name); >> if (c == null) { >> long t0 = System.nanoTime(); >> try { >> if (parent != null) { >> c = parent.loadClass(name, false); >> } else { >> c = findBootstrapClassOrNull(name); >> } >> } catch (ClassNotFoundException e) { >> // ClassNotFoundException thrown if class not found >> // from the non-null parent class loader >> } >> >> if (c == null) { >> // If still not found, then invoke findClass in order >> // to find the class. >> long t1 = System.nanoTime(); >> c = findClass(name); >> >> // this is the defining class loader; record the stats >> sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); >> sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); >> sun.misc.PerfCounter.getFindClasses().increment(); >> } >> } >> if (resolve) { >> resolveClass(c); >> } >> return c; >> } >> } >> >> So I guess it should look like this in Launcher$AppClassLoader, just to >> ensure the same things are done (regardless of whether it's necessary or >> not)? > > In ClassLoader.loadClass it is providing atomicity across a number of actions > in the worst-case: > - checking for already loaded; if not then > - try to load through parent; if not then > - findClass (which will do defineClass) > > You don't have those atomicity constraints because you are only doing one > thing - checking to see if the class is loaded. > > Your locking is probably harmless but those are famous last words when it > comes to classloading. :) > >> >> Does resolveClass need to be done inside the synchronized block? > > Depends on whether it depends on the classloader locking to prevent > concurrent resolve attempts. > > David > ----- > >> class Launcher$AppClassLoader { >> public Class<?> loadClass(String name, boolean resolve) >> throws ClassNotFoundException >> { >> int i = name.lastIndexOf('.'); >> if (i != -1) { >> SecurityManager sm = System.getSecurityManager(); >> if (sm != null) { >> sm.checkPackageAccess(name.substring(0, i)); >> } >> } >> >> if (ucp.knownToNotExist(name)) { >> // The class of the given name is not found in the parent >> // class loader as well as its local URLClassPath. >> // Check if this class has already been defined >> dynamically; >> // if so, return the loaded class; otherwise, skip the >> parent >> // delegation and findClass. >> >>from here >> * synchronized (getClassLoadingLock(name)) {** >> ** Class<?> c = findLoadedClass(name);** >> ** if (c != null) {** >> ** if (resolve) {** >> ** resolveClass(c);** >> ** }** >> ** return c;** >> ** }** >> ** }* >> <<to here >> throw new ClassNotFoundException(name); >> } >> >> return (super.loadClass(name, resolve)); >> } >> >> Thanks >> - Ioi >> >> >>> David >>> >>>> David H and Mandy - does that make sense to you both? >>>> >>>> thanks, >>>> Karen >>>> >>>> On Oct 28, 2014, at 12:38 AM, Ioi Lam wrote: >>>> >>>>> >>>>> On 10/27/14, 7:04 PM, Mandy Chung wrote: >>>>>> >>>>>> On 10/27/2014 3:32 PM, Ioi Lam wrote: >>>>>>> Hi David, I have update the latest webrev at: >>>>>>> >>>>>>> http://cr.openjdk.java.net/~iklam/8061651-lookup-index-open-v3/ >>>>>>> >>>>>> >>>>>> The update looks good. Thanks. >>>>>> >>>>>>> This version also contains the JDK test case that Mandy requested: >>>>>>> >>>>>>> http://cr.openjdk.java.net/~iklam/8061651-lookup-index-open-v3/jdk/test/sun/misc/URLClassPath/EnableLookupCache.java.html >>>>>>> >>>>>>> >>>>>> >>>>>> What I request to add is a test setting the system property >>>>>> (-Dsun.cds.enableSharedLookupCache=true) and continue to load class >>>>>> A and B. Removing line 44-58 should do it and also no need to set >>>>>> -Dfoo.foo.bar. >>>>>> >>>>> Do you mean change the test to call >>>>> System.setProperty("sun.cds.enableSharedLookupCache", "true")? >>>>> >>>>> But we know that the property is checked only once, before any app >>>>> classes are loaded. So calling System.setProperty in an application >>>>> class won't test anything. >>>>>> It'd be good if you run this test and turn on the debug traces to >>>>>> make sure that the application class loader and ext class loader >>>>>> will start up with the lookup cache enabled and make up call to the >>>>>> VM. As it doesn't have the app cds archive, it will invalidate the >>>>>> cache right away and continue the class lookup with null cache array. >>>>> In the latest code, if CDS is not available, lookupCacheEnabled will >>>>> be set to false inside the static initializer of URLClassPath: >>>>> >>>>> private static volatile boolean lookupCacheEnabled >>>>> = >>>>> "true".equals(VM.getSavedProperty("sun.cds.enableSharedLookupCache")); >>>>> >>>>> later, when the boot/ext/app loaders call into here: >>>>> >>>>> synchronized void initLookupCache(ClassLoader loader) { >>>>> if ((lookupCacheURLs = getLookupCacheURLs(loader)) != null) { >>>>> lookupCacheLoader = loader; >>>>> } else { >>>>> // This JVM instance does not support lookup cache. >>>>> disableAllLookupCaches(); >>>>> } >>>>> } >>>>> >>>>> their lookupCacheURLs[] fields will all be set to null. As a result, >>>>> getLookupCacheForClassLoader and knownToNotExist0 will never be called. >>>>> >>>>> I can add a DEBUG_LOOKUP_CACHE trace inside disableAllLookupCaches >>>>> to print "lookup cache disabled", and check for that in the test. Is >>>>> this OK? >>>>> >>>>> Thanks >>>>> - Ioi >>>>> >>>>> >>>>> >>>> >>