From 5fda98052468d1fed83e198942174513300be943 Mon Sep 17 00:00:00 2001 From: David Estes Date: Fri, 18 Aug 2017 09:58:53 -0400 Subject: [PATCH] Resolving Issue #194 by swapping out a WeakHashMap for a ConcurrentWeakHashMap implementation --- .../loaded/ri/ReflectiveInterceptor.java | 5 +- .../ConcurrentWeakIdentityHashMap.java | 267 ++++++++++++++++++ 2 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 springloaded/src/main/java/org/springsource/loaded/support/ConcurrentWeakIdentityHashMap.java diff --git a/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java b/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java index 46a647d..28d8fd7 100644 --- a/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java +++ b/springloaded/src/main/java/org/springsource/loaded/ri/ReflectiveInterceptor.java @@ -53,7 +53,7 @@ import org.springsource.loaded.TypeRegistry; import org.springsource.loaded.Utils; import org.springsource.loaded.infra.UsedByGeneratedCode; import org.springsource.loaded.jvm.JVM; - +import org.springsource.loaded.support.ConcurrentWeakIdentityHashMap; /** * The reflective interceptor is called to rewrite any reflective calls that are found in the bytecode. Intercepting the @@ -84,7 +84,8 @@ public class ReflectiveInterceptor { classToRType = Collections.synchronizedMap(new WeakHashMap, WeakReference>()); } else { - classToRType = new WeakHashMap, WeakReference>(); + classToRType = new ConcurrentWeakIdentityHashMap, WeakReference>(); + // classToRType = new WeakHashMap, WeakReference>(); } } diff --git a/springloaded/src/main/java/org/springsource/loaded/support/ConcurrentWeakIdentityHashMap.java b/springloaded/src/main/java/org/springsource/loaded/support/ConcurrentWeakIdentityHashMap.java new file mode 100644 index 0000000..0b5f9ac --- /dev/null +++ b/springloaded/src/main/java/org/springsource/loaded/support/ConcurrentWeakIdentityHashMap.java @@ -0,0 +1,267 @@ +/* + * Copyright 2016 zhanhb. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springsource.loaded.support; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * + * @author zhanhb + * @param + * @param + */ +public class ConcurrentWeakIdentityHashMap extends AbstractMap + implements ConcurrentMap { + + private final ConcurrentMap, V> map; + private final ReferenceQueue queue = new ReferenceQueue(); + private transient Set> es; + + public ConcurrentWeakIdentityHashMap(int initialCapacity) { + this.map = new ConcurrentHashMap, V>(initialCapacity); + } + + @SuppressWarnings("CollectionWithoutInitialCapacity") + public ConcurrentWeakIdentityHashMap() { + this.map = new ConcurrentHashMap, V>(); + } + + @Override + public V get(Object key) { + purgeKeys(); + return map.get(new Key(key, null)); + } + + @Override + public V put(K key, V value) { + purgeKeys(); + return map.put(new Key(key, queue), value); + } + + @Override + public int size() { + purgeKeys(); + return map.size(); + } + + @SuppressWarnings({"NestedAssignment", "element-type-mismatch"}) + private void purgeKeys() { + Reference reference; + while ((reference = queue.poll()) != null) { + map.remove(reference); + } + } + + @Override + @SuppressWarnings("NestedAssignment") + public Set> entrySet() { + Set> entrySet; + return ((entrySet = this.es) == null) ? es = new EntrySet() : entrySet; + } + + @Override + public V putIfAbsent(K key, V value) { + purgeKeys(); + return map.putIfAbsent(new Key(key, queue), value); + } + + @Override + public V remove(Object key) { + return map.remove(new Key(key, null)); + } + + @Override + public boolean remove(Object key, Object value) { + purgeKeys(); + return map.remove(new Key(key, null), value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + purgeKeys(); + return map.replace(new Key(key, null), oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + purgeKeys(); + return map.replace(new Key(key, null), value); + } + + @Override + public boolean containsKey(Object key) { + purgeKeys(); + return map.containsKey(new Key(key, null)); + } + + @Override + @SuppressWarnings("empty-statement") + public void clear() { + while (queue.poll() != null); + map.clear(); + } + + @Override + public boolean containsValue(Object value) { + purgeKeys(); + return map.containsValue(value); + } + + private static class Key extends WeakReference { + + private final int hash; + + Key(T t, ReferenceQueue queue) { + super(t, queue); + hash = System.identityHashCode(Objects.requireNonNull(t)); + } + + @Override + public boolean equals(Object obj) { + return this == obj || obj instanceof Key && ((Key) obj).get() == get(); + } + + @Override + public int hashCode() { + return hash; + } + + } + + private class Iter implements Iterator> { + + private final Iterator, V>> it; + private Map.Entry nextValue; + + Iter(Iterator, V>> it) { + this.it = it; + } + + @Override + public boolean hasNext() { + if (nextValue != null) { + return true; + } + while (it.hasNext()) { + Map.Entry, V> entry = it.next(); + K key = entry.getKey().get(); + if (key != null) { + nextValue = new Entry(key, entry.getValue()); + return true; + } else { + it.remove(); + } + } + return false; + } + + @Override + public Map.Entry next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + Map.Entry entry = nextValue; + nextValue = null; + return entry; + } + + @Override + public void remove() { + it.remove(); + nextValue = null; + } + + } + + private class EntrySet extends AbstractSet> { + + @Override + public Iterator> iterator() { + return new Iter(map.entrySet().iterator()); + } + + @Override + public int size() { + return ConcurrentWeakIdentityHashMap.this.size(); + } + + @Override + public void clear() { + ConcurrentWeakIdentityHashMap.this.clear(); + } + + @Override + @SuppressWarnings("element-type-mismatch") + public boolean contains(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + return ConcurrentWeakIdentityHashMap.this.get(e.getKey()) == e.getValue(); + } + + @Override + public boolean remove(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry) o; + return ConcurrentWeakIdentityHashMap.this.remove(e.getKey(), e.getValue()); + } + } + + private class Entry extends AbstractMap.SimpleEntry { + + private static final long serialVersionUID = 1L; + + Entry(K key, V value) { + super(key, value); + } + + @Override + public V setValue(V value) { + ConcurrentWeakIdentityHashMap.this.put(getKey(), value); + return super.setValue(value); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Map.Entry) { + Map.Entry e = (Map.Entry) obj; + return getKey() == e.getKey() && getValue() == e.getValue(); + } + return false; + } + + @Override + public int hashCode() { + return System.identityHashCode(getKey()) + ^ System.identityHashCode(getValue()); + } + } + +} \ No newline at end of file