RFR: 8060130: Simplify the synchronization of defining and getting java.lang.Package
Claes Redestad
claes.redestad at oracle.com
Mon Oct 13 15:06:50 UTC 2014
Hi Peter,
On 10/13/2014 04:32 PM, Peter Levart wrote:
> On 10/12/2014 04:09 AM, Claes Redestad wrote:
>>
>>
>> http://cr.openjdk.java.net/~redestad/8060130/webrev.03/
>>
>
> Hi Claes,
>
> First, one more nit:
>
> 1635 if (!map.containsKey(pkgName)) {
> 1636 map.put(pkgName, pkg);
> 1637 }
>
> ..could be written as:
>
> map.putIfAbsent(pkgName, pkg);
>
Nice catch!
>
> And second, I re-read the code of CachedManifest and noticed the
> following. You initialize the volatile instance field 'manifest' like
> that:
>
> 609 private volatile Manifest manifest = EMPTY_MANIFEST;
>
>
> So in following code:
>
> 631 public Manifest getManifest() {
> 632 resolveManifest();
> 633 return manifest;
> 634 }
> 635
> 636 private void resolveManifest() {
> 637 if (manifest != null || url == null) {
> 638 return;
> 639 }
> 640 synchronized(this) {
> 641 if (manifest == null) {
> 642 manifest = AccessController.doPrivileged(new
> PrivilegedAction<Manifest>() {
> 643 public Manifest run() {
> 644 return loadManifest(fileName);
> 645 }
> 646 });
> 647 }
> 648 }
> 649 }
>
>
>
> ... loadManifest() will never be executed. getManifest() will always
> return EMPTY_MANIFEST.
>
> You probably wanted line 637 to be written as:
>
> if (manivest != EMPTY_MANIFEST || url == null) { ...
>
>
> Likewise in loadManifest(), wou write:
>
> 586 private static Manifest loadManifest(String fn) {
> 587 try (FileInputStream fis = new FileInputStream(fn);
> 588 JarInputStream jis = new JarInputStream(fis, false))
> 589 {
> 590 return jis.getManifest();
> 591 } catch (IOException e) {
> 592 return EMPTY_MANIFEST;
> 593 }
> 594 }
>
> ..but you probably wanted the line 592 to return 'null' instead.
Yes, I realized I got this messed up and have prepared a new patch which
passes tests and includes your previous
comments:
http://cr.openjdk.java.net/~redestad/8060130/webrev.04
Aleksey Shipilev pointed out offline that we can't trust the volatile to
be completely initialized here (requiring a
null check either way), so the better solution is to initialize manifest
to null and set it to EMPTY_MANIFEST as
we resolve and consistently only do null checks.
Inlining resolveManifest and loadManifest into getManifest as you
suggested seems to get rid of a few generated
access methods. It's cutting corners on line length limits, though, but
maybe it can be allowed in this case?
By also moving the static final EMPTY_MANIFEST into CachedManifest we
avoid generating any access method for
this as well, with no impact on readability.
For reference I also did some java -verbose:class experiments: any Hello
World program will load most
ConcurrentHashMap classes already - in the same order - and the new
inner class in j.l.Package won't be loaded
unless the application goes to lengths to get a system package, so no
reordering of startup load order is to be
expected. The deviant is getPackages, which when invoked will
additionally load a couple of ConcurrentHashMap
traversal related classes, but I don't think that matters.
Byte code size and complexity seems to drop a bit, too, if anyone's
counting:
javap -v java.lang.Package | wc -l
before: 1263, after: 1139
javap -v java.lang.ClassLoader | wc -l
before: 2800, after: 2687
Thanks!
/Claes
>
>
> Regards, Peter
>
More information about the core-libs-dev
mailing list