From 91d3464cbeeb73084cd8c49826f09fdbaff8f452 Mon Sep 17 00:00:00 2001 From: robm Date: Thu, 5 May 2016 13:30:05 +0100 Subject: [PATCH] 8085903: New fix for memory leak in ProtectionDomain cache Reviewed-by: mullan --- .../java/security/ProtectionDomain.java | 139 ++++++++++++++++-- 1 file changed, 124 insertions(+), 15 deletions(-) diff --git a/src/share/classes/java/security/ProtectionDomain.java b/src/share/classes/java/security/ProtectionDomain.java index b1778490f..d4a7c9ea5 100644 --- a/src/share/classes/java/security/ProtectionDomain.java +++ b/src/share/classes/java/security/ProtectionDomain.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,16 @@ package java.security; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; import sun.misc.JavaSecurityProtectionDomainAccess; import static sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache; import sun.security.util.Debug; @@ -456,25 +460,130 @@ public class ProtectionDomain { /** * Used for storing ProtectionDomains as keys in a Map. */ - final class Key {} + static final class Key {} static { SharedSecrets.setJavaSecurityProtectionDomainAccess( new JavaSecurityProtectionDomainAccess() { + @Override public ProtectionDomainCache getProtectionDomainCache() { - return new ProtectionDomainCache() { - private final Map map = - Collections.synchronizedMap - (new WeakHashMap()); - public void put(ProtectionDomain pd, - PermissionCollection pc) { - map.put((pd == null ? null : pd.key), pc); - } - public PermissionCollection get(ProtectionDomain pd) { - return pd == null ? map.get(null) : map.get(pd.key); - } - }; + return new PDCache(); } }); } + + /** + * A cache of ProtectionDomains and their Permissions. + * + * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap + * with additional support for checking and removing weak keys that are no + * longer in use. There can be cases where the permission collection may + * have a chain of strong references back to the ProtectionDomain, which + * ordinarily would prevent the entry from being removed from the map. To + * address that, we wrap the permission collection in a SoftReference so + * that it can be reclaimed by the garbage collector due to memory demand. + */ + private static class PDCache implements ProtectionDomainCache { + private final ConcurrentHashMap> + pdMap = new ConcurrentHashMap<>(); + private final ReferenceQueue queue = new ReferenceQueue<>(); + + @Override + public void put(ProtectionDomain pd, PermissionCollection pc) { + processQueue(queue, pdMap); + WeakProtectionDomainKey weakPd = + new WeakProtectionDomainKey(pd, queue); + pdMap.put(weakPd, new SoftReference<>(pc)); + } + + @Override + public PermissionCollection get(ProtectionDomain pd) { + processQueue(queue, pdMap); + WeakProtectionDomainKey weakPd = new WeakProtectionDomainKey(pd); + SoftReference sr = pdMap.get(weakPd); + return (sr == null) ? null : sr.get(); + } + + /** + * Removes weak keys from the map that have been enqueued + * on the reference queue and are no longer in use. + */ + private static void processQueue(ReferenceQueue queue, + ConcurrentHashMap, ?> pdMap) { + Reference ref; + while ((ref = queue.poll()) != null) { + pdMap.remove(ref); + } + } + } + + /** + * A weak key for a ProtectionDomain. + */ + private static class WeakProtectionDomainKey extends WeakReference { + /** + * Saved value of the referent's identity hash code, to maintain + * a consistent hash code after the referent has been cleared + */ + private final int hash; + + /** + * A key representing a null ProtectionDomain. + */ + private static final Key NULL_KEY = new Key(); + + /** + * Create a new WeakProtectionDomain with the specified domain and + * registered with a queue. + */ + WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue rq) { + this((pd == null ? NULL_KEY : pd.key), rq); + } + + WeakProtectionDomainKey(ProtectionDomain pd) { + this(pd == null ? NULL_KEY : pd.key); + } + + private WeakProtectionDomainKey(Key key, ReferenceQueue rq) { + super(key, rq); + hash = key.hashCode(); + } + + private WeakProtectionDomainKey(Key key) { + super(key); + hash = key.hashCode(); + } + + /** + * Returns the identity hash code of the original referent. + */ + @Override + public int hashCode() { + return hash; + } + + /** + * Returns true if the given object is an identical + * WeakProtectionDomainKey instance, or, if this object's referent + * has not been cleared and the given object is another + * WeakProtectionDomainKey instance with an identical non-null + * referent as this one. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof WeakProtectionDomainKey) { + Object referent = get(); + return (referent != null) && + (referent == ((WeakProtectionDomainKey)obj).get()); + } else { + return false; + } + } + } } -- GitLab