RFR: 8060130: Simplify the synchronization of defining and getting java.lang.Package

Claes Redestad claes.redestad at oracle.com
Sun Oct 12 02:09:30 UTC 2014


On 2014-10-11 02:31, Mandy Chung wrote:
>
> On 10/10/2014 8:10 AM, Claes Redestad wrote:
>> Hi all,
>>
>> please review this patch which attempts to clean up synchronization 
>> and improve scalability when
>> defining and getting java.lang.Package objects.
>
> I agree with David that getting Package objects are not performance
> critical. On the other hand, the code defining/getting Packages is
> old and deserves some cleanup especially the synchronization part.
>
> If you run helloworld program, how does that change the list of loaded
> classes besides the new CachedManifest class?
>
>>
>> webrev: http://cr.openjdk.java.net/~redestad/8060130/webrev.02/
>
> ClassLoader.java
> line 272: can you change the declared type as Map.

Map misses the atomicity requirement of putIfAbsent, ConcurrentMap is OK 
but leaves some
related questions open (why we can't add a null value, specifically). 
I'm glad it was brought up
and discussed and will use ConcurrentHashMap for private fields unless 
there's a strong
preference otherwise.

>
> definePackage throws IAE if there exists an existing package either
> in this class loader or one of its ancestors.
>   - this change would not catch if two definePackage calls to define
>   a package of the same name but with different spec version, title, etc
>   concurrently.
>
>   You may not be able to avoid synchronizing on packages for this case.

Right, I was I think synchronization can still be avoided by throwing IAE if
putIfAbsent doesn't return null:

         if (packages.putIfAbsent(name, pkg) != null) {
             throw new IllegalArgumentException(name);
         }
         return pkg;

>
> move line 1623 to 1630 so that the declaration of map is closer to
> the assignment.

Ok

>
> Package.java
>
> line 557 there is a possibility that new Package[pkgs.size()] is not
> big enough and a new array would be created.  As this method is not
> popularly used, it's okay if another array is created.

Yes, an unlikely race.

>
> line 563 and 565 can be merged
>
> line 570-575: do you think you can modify the private
>   Package(String name, Manifest man, URL url, ClassLoader loader) 
> constructor
> to take null Manifest and null url so that these lines can be folded into
>
> pkg = new Package(name,  cachedManifest.getManifest(), 
> cachedManifest.getURL(), null);

I think I'll take your suggestion below and ensure cachedManifest and 
it's getManifest()
never evaluate to null, which makes for a cleaner patch. There is some 
code duplication
here with URLClassLoader#definePackage. Future cleanup?

It would seem the ClassLoader argument in this ctor is always called 
with null. Remove?

>
> I think CachedManifest class and the createCachedManifest method need
> some work.  Perhaps we can have the CachedManifest constructor to
> obtain the URL.
>
> Each invalid fn will have one instance instead of NO_MANIFEST singleton
> but that should not happen as fn is the filename where the classes
> loaded from the bootclasspath.  CachedManifest.url can then become final.
>
> line 587-601 would not be needed. Can we avoid line 606 and write
> the createCachedManifest method this way:
>    if (!manifests.containsKey(fn)) {
>        manifests.putIfAbsent(fn, new CachedManifest(fn));
>    }
>    return manifests.get(fn);

Yes. Looked a bit dangerous, but it seems we still maintain the 
necessary guarantees.

>
> You may be able to further simplify CachedManifest and remove the 
> resolved
> field by storing an empty Manifest when loadManifest returns null.
> That may help the private Package constructor not require any change
> to merge line 570-575 as my comment noted above.

Sure! Taking in all these suggestions as well as realizing a race could 
cause different Package
to return from subsequent calls to Package.defineSystemPackage brings me 
to this:

http://cr.openjdk.java.net/~redestad/8060130/webrev.03/

Now... How about a slightly alternative approach: instead of caching 
Manifests we could create
and cache a Package - call it a prototype - then add a private 
constructor taking the
package name and the "prototype" Package. The Package objects should 
come with a
smaller footprint and have the added benefit of being effectively 
immutable. Does that
sound like an improvement?

>
> You will need to check there is any test to verify Package created with
> and without manifest.  Do you mind making this change and tests (I 
> realize
> it might be out of scope of this performance improvement you initially
> anticipated)?

I'll take a look at the current test coverage and give it some thought.

Thanks!

/Claes

>
> Mandy
>




More information about the core-libs-dev mailing list