RFR: 8060130: Simplify the synchronization of defining and getting java.lang.Package
Peter Levart
peter.levart at gmail.com
Sun Oct 12 10:40:53 UTC 2014
On 10/12/2014 04:09 AM, Claes Redestad wrote:
> 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/
>
Hi Claes,
Just some nits...
- I think you're still using Map as the type of 'pkgs' and 'manifests'
static fields in Package although they are CHMs at runtime.
- To avoid unnecessary (multiple) reads from a volatile field, the
following code in CachedManifest:
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 }
could be written as:
public Manifest getManifest() {
if (url == null) return null;
Manifest m = manifest;
if (m != null) return m;
synchronized (this) {
if ((m = manifest) != null) return m;
manifest = m = AccessControler.doPrivileged(....);
return m;
}
}
- It would be better to move loadManifest() into the CachedManifest
class since it's only used there as a helper method (to avoid creating
access bridges or having to declare it package-private instead) - well
no, this doesn't help, since it's called from an anonymous inner
PrivilegedAction subclass. What about just inlining it into the
PrivilegeAction's run() method?
I also wonder if the lazy loading of Manifest in CachedManifest is
necessary. If you look at the only usage of CachedManifest.getManifest()
method (in Package.defineSystemPackage()):
CachedManifest cachedManifest = createCachedManifest(fn);
pkgs.putIfAbsent(name, new Package(name,
cachedManifest.getManifest(),
cachedManifest.getURL()));
...you can see that getManifest() is called immediately after obtaining
the CacheManifest. So there's no need for lazy loading. Loading the
Manifest in CachedManifest constructor would be just fine.
> 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?
Or, call the prototype a 'CachedManifest' and Package just delegate
methods to it. Well...
A noble goal, but this space optimization would only pertain to system
packages if changed only in ClassLoader/Package. You would still have to
have "fat" Packages because other ClassLoaders define their Packages
with the other Package constructor that doesn't take the Manifest file
name (which would be used as a key to obtain a prototype), but use their
own Manifest parsing (see URLClassLoader.definePackage()) ... There are
just a couple of system packages. So you would have to also change the
API between individual ClassLoader(s) and ClassLoader/Package (see
ClassLoader.definePackage()) to optimize other non-system packages which
outnumber system packages. There are not so many packages after all
(compared to Class(es)).
Regards, Peter
More information about the core-libs-dev
mailing list