--- a/jdk/src/share/classes/java/lang/invoke/MethodType.java Thu Jul 12 00:12:28 2012 -0700
+++ b/jdk/src/share/classes/java/lang/invoke/MethodType.java Thu Jul 12 00:12:52 2012 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2012, 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
@@ -26,9 +26,10 @@
package java.lang.invoke;
import sun.invoke.util.Wrapper;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import sun.invoke.util.BytecodeDescriptor;
import static java.lang.invoke.MethodHandleStatics.*;
@@ -135,8 +136,7 @@
return new IndexOutOfBoundsException(num.toString());
}
- static final HashMap<MethodType,MethodType> internTable
- = new HashMap<MethodType, MethodType>();
+ static final WeakInternSet internTable = new WeakInternSet();
static final Class<?>[] NO_PTYPES = {};
@@ -239,11 +239,9 @@
}
MethodType mt1 = new MethodType(rtype, ptypes);
MethodType mt0;
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- }
+ mt0 = internTable.get(mt1);
+ if (mt0 != null)
+ return mt0;
if (!trusted)
// defensively copy the array passed in by the user
mt1 = new MethodType(rtype, ptypes.clone());
@@ -254,15 +252,8 @@
// This is a principal (erased) type; show it to the JVM.
MethodHandleNatives.init(mt1);
}
- synchronized (internTable) {
- mt0 = internTable.get(mt1);
- if (mt0 != null)
- return mt0;
- internTable.put(mt1, mt1);
- }
- return mt1;
+ return internTable.add(mt1);
}
-
private static final MethodType[] objectOnlyTypes = new MethodType[20];
/**
@@ -919,4 +910,269 @@
// Verify all operands, and make sure ptypes is unshared:
return methodType(rtype, ptypes);
}
+
+ /**
+ * Weak intern set based on implementation of the <tt>HashSet</tt> and
+ * <tt>WeakHashMap</tt>, with <em>weak values</em>. Note: <tt>null</tt>
+ * values will yield <tt>NullPointerException</tt>
+ * Refer to implementation of WeakInternSet for details.
+ *
+ * @see java.util.HashMap
+ * @see java.util.HashSet
+ * @see java.util.WeakHashMap
+ * @see java.lang.ref.WeakReference
+ */
+ private static class WeakInternSet {
+ // The default initial capacity -- MUST be a power of two.
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ // The maximum capacity, used if a higher value is implicitly specified
+ // by either of the constructors with arguments.
+ // MUST be a power of two <= 1<<30.
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ // The load factor used when none specified in constructor.
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ // The table, resized as necessary. Length MUST Always be a power of two.
+ private Entry[] table;
+
+ // The number of entries contained in this set.
+ private int size;
+
+ // The next size value at which to resize (capacity * load factor).
+ private int threshold;
+
+ // The load factor for the hash table.
+ private final float loadFactor;
+
+ // Reference queue for cleared WeakEntries
+ private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+
+ private Entry[] newTable(int n) {
+ return new Entry[n];
+ }
+
+ /**
+ * Constructs a new, empty <tt>WeakInternSet</tt> with the default initial
+ * capacity (16) and load factor (0.75).
+ */
+ WeakInternSet() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ threshold = DEFAULT_INITIAL_CAPACITY;
+ table = newTable(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Applies a supplemental hash function to a given hashCode, which
+ * defends against poor quality hash functions. This is critical
+ * because hashing uses power-of-two length hash tables, that
+ * otherwise encounter collisions for hashCodes that do not differ
+ * in lower bits.
+ * @param h preliminary hash code value
+ * @return supplemental hash code value
+ */
+ private static int hash(int h) {
+ // This function ensures that hashCodes that differ only by
+ // constant multiples at each bit position have a bounded
+ // number of collisions (approximately 8 at default load factor).
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /**
+ * Checks for equality of non-null reference x and possibly-null y. By
+ * default uses Object.equals.
+ * @param x first object to compare
+ * @param y second object to compare
+ * @return <tt>true</tt> if objects are equal
+ */
+ private static boolean eq(Object x, Object y) {
+ return x == y || x.equals(y);
+ }
+
+ /**
+ * Returns index for hash code h.
+ * @param h raw hash code
+ * @param length length of table (power of 2)
+ * @return index in table
+ */
+ private static int indexFor(int h, int length) {
+ return h & (length-1);
+ }
+
+ /**
+ * Expunges stale entries from the table.
+ */
+ private void expungeStaleEntries() {
+ for (Object x; (x = queue.poll()) != null; ) {
+ synchronized (queue) {
+ Entry entry = (Entry) x;
+ int i = indexFor(entry.hash, table.length);
+ Entry prev = table[i];
+ Entry p = prev;
+ while (p != null) {
+ Entry next = p.next;
+ if (p == entry) {
+ if (prev == entry)
+ table[i] = next;
+ else
+ prev.next = next;
+ entry.next = null;
+ size--;
+ break;
+ }
+ prev = p;
+ p = next;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the table after first expunging stale entries.
+ * @return an expunged hash table
+ */
+ private Entry[] getTable() {
+ expungeStaleEntries();
+ return table;
+ }
+
+ /**
+ * Returns the entry to which the specified value is mapped,
+ * or {@code null} if this set contains no entry for the value.
+ *
+ * <p>More formally, if this set contains an entry for value
+ * {@code entry} to a value {@code value} such that
+ * {@code entry.equals(value)}, then this method returns {@code entry};
+ * otherwise it returns {@code null}.
+ *
+ * @param value value to search for in set
+ * @return interned value if in set, otherwise <tt>null</tt>
+ */
+ synchronized MethodType get(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int index = indexFor(h, tab.length);
+ Entry e = tab[index];
+ MethodType g;
+ while (e != null) {
+ if (e.hash == h && eq(value, g = e.get()))
+ return g;
+ e = e.next;
+ }
+ return null;
+ }
+
+ /**
+ * Attempts to add the specified value to the set and returns same value.
+ * If the set previously contained an entry for this value, the old
+ * value is left untouched and returned as the result.
+ *
+ * @param value value to be added
+ * @return the previous entry associated with <tt>value</tt>, or
+ * <tt>value</tt> if there was no previous entry found
+ */
+ synchronized MethodType add(MethodType value) {
+ int h = hash(value.hashCode());
+ Entry[] tab = getTable();
+ int i = indexFor(h, tab.length);
+ MethodType g;
+ for (Entry e = tab[i]; e != null; e = e.next) {
+ if (h == e.hash && eq(value, g = e.get())) {
+ return g;
+ }
+ }
+ Entry e = tab[i];
+ tab[i] = new Entry(value, queue, h, e);
+ if (++size >= threshold)
+ resize(tab.length * 2);
+ return value;
+ }
+
+ /**
+ * Rehashes the contents of this set into a new array with a
+ * larger capacity. This method is called automatically when the
+ * number of keys in this set reaches its threshold.
+ *
+ * If current capacity is MAXIMUM_CAPACITY, this method does not
+ * resize the set, but sets threshold to Integer.MAX_VALUE.
+ * This has the effect of preventing future calls.
+ *
+ * @param newCapacity the new capacity, MUST be a power of two;
+ * must be greater than current capacity unless current
+ * capacity is MAXIMUM_CAPACITY (in which case value
+ * is irrelevant)
+ */
+ private void resize(int newCapacity) {
+ Entry[] oldTable = getTable();
+ int oldCapacity = oldTable.length;
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ Entry[] newTable = newTable(newCapacity);
+ transfer(oldTable, newTable);
+ table = newTable;
+
+ /*
+ * 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 (size >= threshold / 2) {
+ threshold = (int)(newCapacity * loadFactor);
+ } else {
+ expungeStaleEntries();
+ transfer(newTable, oldTable);
+ table = oldTable;
+ }
+ }
+
+ /**
+ * Transfers all entries from src to dest tables
+ * @param src original table
+ * @param dest new table
+ */
+ private void transfer(Entry[] src, Entry[] dest) {
+ for (int j = 0; j < src.length; ++j) {
+ Entry e = src[j];
+ src[j] = null;
+ while (e != null) {
+ Entry next = e.next;
+ MethodType key = e.get();
+ if (key == null) {
+ e.next = null; // Help GC
+ size--;
+ } else {
+ int i = indexFor(e.hash, dest.length);
+ e.next = dest[i];
+ dest[i] = e;
+ }
+ e = next;
+ }
+ }
+ }
+
+ /**
+ * The entries in this hash table extend WeakReference, using its main ref
+ * field as the key.
+ */
+ private static class Entry extends WeakReference<MethodType> {
+ final int hash;
+ Entry next;
+
+ /**
+ * Creates new entry.
+ */
+ Entry(MethodType key,
+ ReferenceQueue<Object> queue,
+ int hash, Entry next) {
+ super(key, queue);
+ this.hash = hash;
+ this.next = next;
+ }
+ }
+ }
}