From 406bc60cb7f7886b6515ff8a7cdc25cf01300d73 Mon Sep 17 00:00:00 2001 From: malenkov Date: Fri, 8 Feb 2013 17:32:25 +0400 Subject: [PATCH] 7200507: Refactor Introspector internals Reviewed-by: ahgross, art --- .../java/beans/ThreadGroupContext.java | 7 +- .../classes/java/beans/WeakIdentityMap.java | 181 ++++++++++++++++++ 2 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 src/share/classes/java/beans/WeakIdentityMap.java diff --git a/src/share/classes/java/beans/ThreadGroupContext.java b/src/share/classes/java/beans/ThreadGroupContext.java index dc1d38a14..6236ec2b3 100644 --- a/src/share/classes/java/beans/ThreadGroupContext.java +++ b/src/share/classes/java/beans/ThreadGroupContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2013, 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 @@ -29,7 +29,6 @@ import com.sun.beans.finder.BeanInfoFinder; import com.sun.beans.finder.PropertyEditorFinder; import java.awt.GraphicsEnvironment; -import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; @@ -42,7 +41,7 @@ import java.util.WeakHashMap; */ final class ThreadGroupContext { - private static final Map contexts = new WeakHashMap<>(); + private static final WeakIdentityMap contexts = new WeakIdentityMap<>(); /** * Returns the appropriate {@code AppContext} for the caller, @@ -69,6 +68,8 @@ final class ThreadGroupContext { private BeanInfoFinder beanInfoFinder; private PropertyEditorFinder propertyEditorFinder; + private ThreadGroupContext() { + } boolean isDesignTime() { return this.isDesignTime; diff --git a/src/share/classes/java/beans/WeakIdentityMap.java b/src/share/classes/java/beans/WeakIdentityMap.java new file mode 100644 index 000000000..42ac821a3 --- /dev/null +++ b/src/share/classes/java/beans/WeakIdentityMap.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.beans; + +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; + +/** + * Hash table based mapping, which uses weak references to store keys + * and reference-equality in place of object-equality to compare them. + * An entry will automatically be removed when its key is no longer + * in ordinary use. Both null values and the null key are supported. + * + * @see java.util.IdentityHashMap + * @see java.util.WeakHashMap + */ +final class WeakIdentityMap { + + private static final int MAXIMUM_CAPACITY = 1 << 30; // it MUST be a power of two + private static final Object NULL = new Object(); // special object for null key + + private final ReferenceQueue queue = new ReferenceQueue(); + + private Entry[] table = newTable(1<<3); // table's length MUST be a power of two + private int threshold = 6; // the next size value at which to resize + private int size = 0; // the number of key-value mappings + + public T get(Object key) { + removeStaleEntries(); + if (key == null) { + key = NULL; + } + int hash = key.hashCode(); + int index = getIndex(this.table, hash); + for (Entry entry = this.table[index]; entry != null; entry = entry.next) { + if (entry.isMatched(key, hash)) { + return entry.value; + } + } + return null; + } + + public T put(Object key, T value) { + removeStaleEntries(); + if (key == null) { + key = NULL; + } + int hash = key.hashCode(); + int index = getIndex(this.table, hash); + for (Entry entry = this.table[index]; entry != null; entry = entry.next) { + if (entry.isMatched(key, hash)) { + T oldValue = entry.value; + entry.value = value; + return oldValue; + } + } + this.table[index] = new Entry(key, hash, value, this.queue, this.table[index]); + if (++this.size >= this.threshold) { + if (this.table.length == MAXIMUM_CAPACITY) { + this.threshold = Integer.MAX_VALUE; + } + else { + removeStaleEntries(); + Entry[] table = newTable(this.table.length * 2); + transfer(this.table, table); + + // If ignoring null elements and processing ref queue caused massive + // shrinkage, then restore old table. This should be rare, but avoids + // unbounded expansion of garbage-filled tables. + if (this.size >= this.threshold / 2) { + this.table = table; + this.threshold *= 2; + } + else { + transfer(table, this.table); + } + } + } + return null; + } + + private void removeStaleEntries() { + for (Object ref = this.queue.poll(); ref != null; ref = this.queue.poll()) { + @SuppressWarnings("unchecked") + Entry entry = (Entry) ref; + int index = getIndex(this.table, entry.hash); + + Entry prev = this.table[index]; + Entry current = prev; + while (current != null) { + Entry next = current.next; + if (current == entry) { + if (prev == entry) { + this.table[index] = next; + } + else { + prev.next = next; + } + entry.value = null; // Help GC + entry.next = null; // Help GC + this.size--; + break; + } + prev = current; + current = next; + } + } + } + + private void transfer(Entry[] oldTable, Entry[] newTable) { + for (int i = 0; i < oldTable.length; i++) { + Entry entry = oldTable[i]; + oldTable[i] = null; + while (entry != null) { + Entry next = entry.next; + Object key = entry.get(); + if (key == null) { + entry.value = null; // Help GC + entry.next = null; // Help GC + this.size--; + } + else { + int index = getIndex(newTable, entry.hash); + entry.next = newTable[index]; + newTable[index] = entry; + } + entry = next; + } + } + } + + + @SuppressWarnings("unchecked") + private Entry[] newTable(int length) { + return (Entry[]) new Entry[length]; + } + + private static int getIndex(Entry[] table, int hash) { + return hash & (table.length - 1); + } + + private static class Entry extends WeakReference { + private final int hash; + private T value; + private Entry next; + + Entry(Object key, int hash, T value, ReferenceQueue queue, Entry next) { + super(key, queue); + this.hash = hash; + this.value = value; + this.next = next; + } + + boolean isMatched(Object key, int hash) { + return (this.hash == hash) && (key == get()); + } + } +} -- GitLab