[prev in list] [next in list] [prev in thread] [next in thread]
List: jakarta-commons-dev
Subject: [jira] [Updated] (BEANUTILS-539) use ConcurrentHashMap insteadof WeakFastHashMap
From: "JIN XU (Jira)" <jira () apache ! org>
Date: 2020-05-31 7:16:00
Message-ID: JIRA.13308545.1590908533000.139736.1590909360035 () Atlassian ! JIRA
[Download RAW message or body]
[ https://issues.apache.org/jira/browse/BEANUTILS-539?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel \
]
JIN XU updated BEANUTILS-539:
-----------------------------
Description:
Hi.
throughtout my performance test (using Jprofiler), I found out WeakHashMap is far \
slower than ConcurrentHashMap.
Should we use ConcurrentHashMap instead of WeakHashMap?
I will attach my test codes here.
{code:java}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.beanutils2;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* <p>A customized implementation of {@code java.util.HashMap} designed
* to operate in a multi-threaded environment where the large majority of
* method calls are read-only, instead of structural changes. When operating
* in "fast" mode, read calls are non-synchronized and write calls perform the
* following steps:</p>
* <ul>
* <li>Clone the existing collection
* <li>Perform the modification on the clone
* <li>Replace the existing collection with the (modified) clone
* </ul>
* <p>When first created, objects of this class default to "slow" mode, where
* all accesses of any type are synchronized but no cloning takes place. This
* is appropriate for initially populating the collection, followed by a switch
* to "fast" mode (by calling {@code setFast(true)}) after initialization
* is complete.</p>
*
* <p><strong>NOTE</strong>: If you are creating and accessing a
* {@code HashMap} only within a single thread, you should use
* {@code java.util.HashMap} directly (with no synchronization), for
* maximum performance.</p>
*
* <p><strong>NOTE</strong>: <i>This class is not cross-platform.
* Using it may cause unexpected failures on some architectures.</i>
* It suffers from the same problems as the double-checked locking idiom.
* In particular, the instruction that clones the internal collection and the
* instruction that sets the internal reference to the clone can be executed
* or perceived out-of-order. This means that any read operation might fail
* unexpectedly, as it may be reading the state of the internal collection
* before the internal collection is fully formed.
* For more information on the double-checked locking idiom, see the
* <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html">
* Double-Checked Locking Idiom Is Broken Declaration</a>.</p>
*
* @since Commons Collections 1.0
*/
public class WeakFastHashMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* The underlying map we are managing.
*/
private volatile Map<K, V> map = null;
/**
* Are we currently operating in "fast" mode?
*/
private boolean fast = false;
// Constructors
/**
* Construct an empty map.
*/
public WeakFastHashMap() {
super();
this.map = createMap();
}
/**
* Construct an empty map with the specified capacity.
*
* @param capacity the initial capacity of the empty map
*/
public WeakFastHashMap(final int capacity) {
super();
this.map = createMap(capacity);
}
/**
* Construct an empty map with the specified capacity and load factor.
*
* @param capacity the initial capacity of the empty map
* @param factor the load factor of the new map
*/
public WeakFastHashMap(final int capacity, final float factor) {
super();
this.map = createMap(capacity, factor);
}
/**
* Construct a new map with the same mappings as the specified map.
*
* @param map the map whose mappings are to be copied
*/
public WeakFastHashMap(final Map<? extends K, ? extends V> map) {
super();
this.map = createMap(map);
}
// Property access
/**
* Returns true if this map is operating in fast mode.
*
* @return true if this map is operating in fast mode
*/
public boolean getFast() {
return this.fast;
}
/**
* Sets whether this map is operating in fast mode.
*
* @param fast true if this map should operate in fast mode
*/
public void setFast(final boolean fast) {
this.fast = fast;
}
// Map access
// These methods can forward straight to the wrapped Map in 'fast' mode.
// (because they are query methods)
/**
* Return the value to which this map maps the specified key. Returns
* {@code null} if the map contains no mapping for this key, or if
* there is a mapping with a value of {@code null}. Use the
* {@code containsKey()} method to disambiguate these cases.
*
* @param key the key whose value is to be returned
* @return the value mapped to that key, or null
*/
@Override
public V get(final Object key) {
if (fast) {
return map.get(key);
}
synchronized (map) {
return map.get(key);
}
}
/**
* Return the number of key-value mappings in this map.
*
* @return the current size of the map
*/
@Override
public int size() {
if (fast) {
return map.size();
}
synchronized (map) {
return map.size();
}
}
/**
* Return {@code true} if this map contains no mappings.
*
* @return is the map currently empty
*/
@Override
public boolean isEmpty() {
if (fast) {
return map.isEmpty();
}
synchronized (map) {
return map.isEmpty();
}
}
/**
* Return {@code true} if this map contains a mapping for the
* specified key.
*
* @param key the key to be searched for
* @return true if the map contains the key
*/
@Override
public boolean containsKey(final Object key) {
if (fast) {
return map.containsKey(key);
}
synchronized (map) {
return map.containsKey(key);
}
}
/**
* Return {@code true} if this map contains one or more keys mapping
* to the specified value.
*
* @param value the value to be searched for
* @return true if the map contains the value
*/
@Override
public boolean containsValue(final Object value) {
if (fast) {
return map.containsValue(value);
}
synchronized (map) {
return map.containsValue(value);
}
}
// Map modification
// These methods perform special behavior in 'fast' mode.
// The map is cloned, updated and then assigned back.
// See the comments at the top as to why this won't always work.
/**
* Associate the specified value with the specified key in this map.
* If the map previously contained a mapping for this key, the old
* value is replaced and returned.
*
* @param key the key with which the value is to be associated
* @param value the value to be associated with this key
* @return the value previously mapped to the key, or null
*/
@Override
public V put(final K key, final V value) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
final V result = temp.put(key, value);
map = temp;
return result;
}
}
synchronized (map) {
return map.put(key, value);
}
}
/**
* Copy all of the mappings from the specified map to this one, replacing
* any mappings with the same keys.
*
* @param in the map whose mappings are to be copied
*/
@Override
public void putAll(final Map<? extends K, ? extends V> in) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
temp.putAll(in);
map = temp;
}
} else {
synchronized (map) {
map.putAll(in);
}
}
}
/**
* Remove any mapping for this key, and return any previously
* mapped value.
*
* @param key the key whose mapping is to be removed
* @return the value removed, or null
*/
@Override
public V remove(final Object key) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
final V result = temp.remove(key);
map = temp;
return result;
}
}
synchronized (map) {
return map.remove(key);
}
}
/**
* Remove all mappings from this map.
*/
@Override
public void clear() {
if (fast) {
synchronized (this) {
map = createMap();
}
} else {
synchronized (map) {
map.clear();
}
}
}
// Basic object methods
/**
* Compare the specified object with this list for equality. This
* implementation uses exactly the code that is used to define the
* list equals function in the documentation for the
* {@code Map.equals} method.
*
* @param o the object to be compared to this list
* @return true if the two maps are equal
*/
@Override
public boolean equals(final Object o) {
// Simple tests that require no synchronization
if (o == this) {
return true;
} else if (!(o instanceof Map)) {
return false;
}
final Map<?, ?> mo = (Map<?, ?>) o;
// Compare the two maps for equality
if (fast) {
if (mo.size() != map.size()) {
return false;
}
for (final Map.Entry<K, V> e : map.entrySet()) {
final K key = e.getKey();
final V value = e.getValue();
if (value == null) {
if (!(mo.get(key) == null && mo.containsKey(key))) {
return false;
}
} else {
if (!value.equals(mo.get(key))) {
return false;
}
}
}
return true;
}
synchronized (map) {
if (mo.size() != map.size()) {
return false;
}
for (final Map.Entry<K, V> e : map.entrySet()) {
final K key = e.getKey();
final V value = e.getValue();
if (value == null) {
if (!(mo.get(key) == null && mo.containsKey(key))) {
return false;
}
} else {
if (!value.equals(mo.get(key))) {
return false;
}
}
}
return true;
}
}
/**
* Return the hash code value for this map. This implementation uses
* exactly the code that is used to define the list hash function in the
* documentation for the {@code Map.hashCode} method.
*
* @return suitable integer hash code
*/
@Override
public int hashCode() {
if (fast) {
int h = 0;
for (final Map.Entry<K, V> e : map.entrySet()) {
h += e.hashCode();
}
return h;
}
synchronized (map) {
int h = 0;
for (final Map.Entry<K, V> e : map.entrySet()) {
h += e.hashCode();
}
return h;
}
}
/**
* Return a shallow copy of this {@code FastHashMap} instance.
* The keys and values themselves are not copied.
*
* @return a clone of this map
*/
@Override
public Object clone() {
WeakFastHashMap<K, V> results = null;
if (fast) {
results = new WeakFastHashMap<>(map);
} else {
synchronized (map) {
results = new WeakFastHashMap<>(map);
}
}
results.setFast(getFast());
return results;
}
// Map views
/**
* Return a collection view of the mappings contained in this map. Each
* element in the returned collection is a {@code Map.Entry}.
* @return the set of map Map entries
*/
@Override
public Set<Map.Entry<K, V>> entrySet() {
return new EntrySet();
}
/**
* Return a set view of the keys contained in this map.
* @return the set of the Map's keys
*/
@Override
public Set<K> keySet() {
return new KeySet();
}
/**
* Return a collection view of the values contained in this map.
* @return the set of the Map's values
*/
@Override
public Collection<V> values() {
return new Values();
}
// Abstractions on Map creations (for subclasses such as WeakFastHashMap)
protected Map<K, V> createMap() {
return new WeakHashMap<>();
}
protected Map<K, V> createMap(final int capacity) {
return new WeakHashMap<>(capacity);
}
protected Map<K, V> createMap(final int capacity, final float factor) {
return new WeakHashMap<>(capacity, factor);
}
protected Map<K, V> createMap(final Map<? extends K, ? extends V> map) {
return new WeakHashMap<>(map);
}
protected Map<K, V> cloneMap(final Map<? extends K, ? extends V> map) {
return createMap(map);
}
// Map view inner classes
/**
* Abstract collection implementation shared by keySet(), values() and \
entrySet().
*
* @param <E> the element type
*/
private abstract class CollectionView<E> implements Collection<E> {
public CollectionView() {
}
protected abstract Collection<E> get(Map<K, V> map);
protected abstract E iteratorNext(Map.Entry<K, V> entry);
@Override
public void clear() {
if (fast) {
synchronized (WeakFastHashMap.this) {
map = createMap();
}
} else {
synchronized (map) {
get(map).clear();
}
}
}
@Override
public boolean remove(final Object o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).remove(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).remove(o);
}
}
@Override
public boolean removeAll(final Collection<?> o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).removeAll(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).removeAll(o);
}
}
@Override
public boolean retainAll(final Collection<?> o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).retainAll(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).retainAll(o);
}
}
@Override
public int size() {
if (fast) {
return get(map).size();
}
synchronized (map) {
return get(map).size();
}
}
@Override
public boolean isEmpty() {
if (fast) {
return get(map).isEmpty();
}
synchronized (map) {
return get(map).isEmpty();
}
}
@Override
public boolean contains(final Object o) {
if (fast) {
return get(map).contains(o);
}
synchronized (map) {
return get(map).contains(o);
}
}
@Override
public boolean containsAll(final Collection<?> o) {
if (fast) {
return get(map).containsAll(o);
}
synchronized (map) {
return get(map).containsAll(o);
}
}
@Override
public <T> T[] toArray(final T[] o) {
if (fast) {
return get(map).toArray(o);
}
synchronized (map) {
return get(map).toArray(o);
}
}
@Override
public Object[] toArray() {
if (fast) {
return get(map).toArray();
}
synchronized (map) {
return get(map).toArray();
}
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (fast) {
return get(map).equals(o);
}
synchronized (map) {
return get(map).equals(o);
}
}
@Override
public int hashCode() {
if (fast) {
return get(map).hashCode();
}
synchronized (map) {
return get(map).hashCode();
}
}
@Override
public boolean add(final E o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<E> iterator() {
return new CollectionViewIterator();
}
private class CollectionViewIterator implements Iterator<E> {
private Map<K, V> expected;
private Map.Entry<K, V> lastReturned = null;
private final Iterator<Map.Entry<K, V>> iterator;
public CollectionViewIterator() {
this.expected = map;
this.iterator = expected.entrySet().iterator();
}
@Override
public boolean hasNext() {
if (expected != map) {
throw new ConcurrentModificationException();
}
return iterator.hasNext();
}
@Override
public E next() {
if (expected != map) {
throw new ConcurrentModificationException();
}
lastReturned = iterator.next();
return iteratorNext(lastReturned);
}
@Override
public void remove() {
if (lastReturned == null) {
throw new IllegalStateException();
}
if (fast) {
synchronized (WeakFastHashMap.this) {
if (expected != map) {
throw new ConcurrentModificationException();
}
WeakFastHashMap.this.remove(lastReturned.getKey());
lastReturned = null;
expected = map;
}
} else {
iterator.remove();
lastReturned = null;
}
}
}
}
/**
* Set implementation over the keys of the FastHashMap
*/
private class KeySet extends CollectionView<K> implements Set<K> {
@Override
protected Collection<K> get(final Map<K, V> map) {
return map.keySet();
}
@Override
protected K iteratorNext(final Map.Entry<K, V> entry) {
return entry.getKey();
}
}
/**
* Collection implementation over the values of the FastHashMap
*/
private class Values extends CollectionView<V> {
@Override
protected Collection<V> get(final Map<K, V> map) {
return map.values();
}
@Override
protected V iteratorNext(final Map.Entry<K, V> entry) {
return entry.getValue();
}
}
/**
* Set implementation over the entries of the FastHashMap
*/
private class EntrySet extends CollectionView<Map.Entry<K, V>> implements \
Set<Map.Entry<K, V>> {
@Override
protected Collection<Map.Entry<K, V>> get(final Map<K, V> map) {
return map.entrySet();
}
@Override
protected Map.Entry<K, V> iteratorNext(final Map.Entry<K, V> entry) {
return entry;
}
}
}
{code}
and, Jprofiler Call Tree:
h2. Call Tree
> *Session:*|WeakFastHashMapTest|
> *Time of export:*|Sunday, May 31, 2020 2:56:14 PM CST|
> *JVM time:*|12:23|
> > >
> *View mode:* |Tree|
> *Thread selection:* |<img "height="16" width="16" border="0" hspace="0" vspace="0" \
> src=" jprofiler_images/selector_group_16.png"> All thread groups|
> *Thread status:* |<img "height="6" width="16" border="0" hspace="0" vspace="0" \
> src=" jprofiler_images/ff00c400_bff000000.png"> Runnable|
> *Aggregation level:* |Methods|
----
> !jprofiler_images/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=49,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|width=1,height=7,vspace=2! \
> 100.0% - 743 s - 1 inv. WeakFastHashMapTest.main|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 157,464 inv. org.apache.commons.beanutils2.WeakFastHashMap.put|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_te \
> e_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 107,464 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.cloneMap|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_ba \
> r_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_minus_18.g \
> if|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 107,464 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_ba \
> r_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/pixel_transparent_1.gi \
> f|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_18.gif|width=18,h \
> eight=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|width=48,height=7,vspace=2! 96.6% - 718 s \
> - 107,464 inv. java.util.WeakHashMap.<init>|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_co \
> rner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 80,342 µs - \
> 157,463 inv. java.util.Map.put|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.5% - 3,625 ms - \
> 10,654,869 inv. org.apache.commons.beanutils2.WeakFastHashMap.get|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_co \
> rner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.3% - 2,263 ms - \
> 10,654,869 inv. java.util.Map.get|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 1 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.setFast|
> !jprofiler_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 2,776 µs - 1 \
> inv. WeakFastHashMapTest.<clinit>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 51 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.<init>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 36 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_c \
> orner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 7 µs - 1 inv. \
> java.util.WeakHashMap.<init>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 3 µs - 1 inv. \
> java.util.HashMap.<init>|
96.6% of the total running time is at WeakHashMap.put
And notice that only 1/100 chance we write, other 99/100 chance we read.
If you want the data about 1/1000 or other, I will be happy to run/provide.
[https://github.com/apache/commons-beanutils/pull/27]
was:
Hi.
throughtout my performance test (using Jprofiler), I found out WeakHashMap is far \
slower than ConcurrentHashMap.
Should we use ConcurrentHashMap instead of WeakHashMap?
I will attach my test codes here.
{code:java}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.commons.beanutils2;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* <p>A customized implementation of {@code java.util.HashMap} designed
* to operate in a multi-threaded environment where the large majority of
* method calls are read-only, instead of structural changes. When operating
* in "fast" mode, read calls are non-synchronized and write calls perform the
* following steps:</p>
* <ul>
* <li>Clone the existing collection
* <li>Perform the modification on the clone
* <li>Replace the existing collection with the (modified) clone
* </ul>
* <p>When first created, objects of this class default to "slow" mode, where
* all accesses of any type are synchronized but no cloning takes place. This
* is appropriate for initially populating the collection, followed by a switch
* to "fast" mode (by calling {@code setFast(true)}) after initialization
* is complete.</p>
*
* <p><strong>NOTE</strong>: If you are creating and accessing a
* {@code HashMap} only within a single thread, you should use
* {@code java.util.HashMap} directly (with no synchronization), for
* maximum performance.</p>
*
* <p><strong>NOTE</strong>: <i>This class is not cross-platform.
* Using it may cause unexpected failures on some architectures.</i>
* It suffers from the same problems as the double-checked locking idiom.
* In particular, the instruction that clones the internal collection and the
* instruction that sets the internal reference to the clone can be executed
* or perceived out-of-order. This means that any read operation might fail
* unexpectedly, as it may be reading the state of the internal collection
* before the internal collection is fully formed.
* For more information on the double-checked locking idiom, see the
* <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html">
* Double-Checked Locking Idiom Is Broken Declaration</a>.</p>
*
* @since Commons Collections 1.0
*/
public class WeakFastHashMap<K, V> extends HashMap<K, V> {
private static final long serialVersionUID = 1L;
/**
* The underlying map we are managing.
*/
private volatile Map<K, V> map = null;
/**
* Are we currently operating in "fast" mode?
*/
private boolean fast = false;
// Constructors
/**
* Construct an empty map.
*/
public WeakFastHashMap() {
super();
this.map = createMap();
}
/**
* Construct an empty map with the specified capacity.
*
* @param capacity the initial capacity of the empty map
*/
public WeakFastHashMap(final int capacity) {
super();
this.map = createMap(capacity);
}
/**
* Construct an empty map with the specified capacity and load factor.
*
* @param capacity the initial capacity of the empty map
* @param factor the load factor of the new map
*/
public WeakFastHashMap(final int capacity, final float factor) {
super();
this.map = createMap(capacity, factor);
}
/**
* Construct a new map with the same mappings as the specified map.
*
* @param map the map whose mappings are to be copied
*/
public WeakFastHashMap(final Map<? extends K, ? extends V> map) {
super();
this.map = createMap(map);
}
// Property access
/**
* Returns true if this map is operating in fast mode.
*
* @return true if this map is operating in fast mode
*/
public boolean getFast() {
return this.fast;
}
/**
* Sets whether this map is operating in fast mode.
*
* @param fast true if this map should operate in fast mode
*/
public void setFast(final boolean fast) {
this.fast = fast;
}
// Map access
// These methods can forward straight to the wrapped Map in 'fast' mode.
// (because they are query methods)
/**
* Return the value to which this map maps the specified key. Returns
* {@code null} if the map contains no mapping for this key, or if
* there is a mapping with a value of {@code null}. Use the
* {@code containsKey()} method to disambiguate these cases.
*
* @param key the key whose value is to be returned
* @return the value mapped to that key, or null
*/
@Override
public V get(final Object key) {
if (fast) {
return map.get(key);
}
synchronized (map) {
return map.get(key);
}
}
/**
* Return the number of key-value mappings in this map.
*
* @return the current size of the map
*/
@Override
public int size() {
if (fast) {
return map.size();
}
synchronized (map) {
return map.size();
}
}
/**
* Return {@code true} if this map contains no mappings.
*
* @return is the map currently empty
*/
@Override
public boolean isEmpty() {
if (fast) {
return map.isEmpty();
}
synchronized (map) {
return map.isEmpty();
}
}
/**
* Return {@code true} if this map contains a mapping for the
* specified key.
*
* @param key the key to be searched for
* @return true if the map contains the key
*/
@Override
public boolean containsKey(final Object key) {
if (fast) {
return map.containsKey(key);
}
synchronized (map) {
return map.containsKey(key);
}
}
/**
* Return {@code true} if this map contains one or more keys mapping
* to the specified value.
*
* @param value the value to be searched for
* @return true if the map contains the value
*/
@Override
public boolean containsValue(final Object value) {
if (fast) {
return map.containsValue(value);
}
synchronized (map) {
return map.containsValue(value);
}
}
// Map modification
// These methods perform special behavior in 'fast' mode.
// The map is cloned, updated and then assigned back.
// See the comments at the top as to why this won't always work.
/**
* Associate the specified value with the specified key in this map.
* If the map previously contained a mapping for this key, the old
* value is replaced and returned.
*
* @param key the key with which the value is to be associated
* @param value the value to be associated with this key
* @return the value previously mapped to the key, or null
*/
@Override
public V put(final K key, final V value) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
final V result = temp.put(key, value);
map = temp;
return result;
}
}
synchronized (map) {
return map.put(key, value);
}
}
/**
* Copy all of the mappings from the specified map to this one, replacing
* any mappings with the same keys.
*
* @param in the map whose mappings are to be copied
*/
@Override
public void putAll(final Map<? extends K, ? extends V> in) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
temp.putAll(in);
map = temp;
}
} else {
synchronized (map) {
map.putAll(in);
}
}
}
/**
* Remove any mapping for this key, and return any previously
* mapped value.
*
* @param key the key whose mapping is to be removed
* @return the value removed, or null
*/
@Override
public V remove(final Object key) {
if (fast) {
synchronized (this) {
final Map<K, V> temp = cloneMap(map);
final V result = temp.remove(key);
map = temp;
return result;
}
}
synchronized (map) {
return map.remove(key);
}
}
/**
* Remove all mappings from this map.
*/
@Override
public void clear() {
if (fast) {
synchronized (this) {
map = createMap();
}
} else {
synchronized (map) {
map.clear();
}
}
}
// Basic object methods
/**
* Compare the specified object with this list for equality. This
* implementation uses exactly the code that is used to define the
* list equals function in the documentation for the
* {@code Map.equals} method.
*
* @param o the object to be compared to this list
* @return true if the two maps are equal
*/
@Override
public boolean equals(final Object o) {
// Simple tests that require no synchronization
if (o == this) {
return true;
} else if (!(o instanceof Map)) {
return false;
}
final Map<?, ?> mo = (Map<?, ?>) o;
// Compare the two maps for equality
if (fast) {
if (mo.size() != map.size()) {
return false;
}
for (final Map.Entry<K, V> e : map.entrySet()) {
final K key = e.getKey();
final V value = e.getValue();
if (value == null) {
if (!(mo.get(key) == null && mo.containsKey(key))) {
return false;
}
} else {
if (!value.equals(mo.get(key))) {
return false;
}
}
}
return true;
}
synchronized (map) {
if (mo.size() != map.size()) {
return false;
}
for (final Map.Entry<K, V> e : map.entrySet()) {
final K key = e.getKey();
final V value = e.getValue();
if (value == null) {
if (!(mo.get(key) == null && mo.containsKey(key))) {
return false;
}
} else {
if (!value.equals(mo.get(key))) {
return false;
}
}
}
return true;
}
}
/**
* Return the hash code value for this map. This implementation uses
* exactly the code that is used to define the list hash function in the
* documentation for the {@code Map.hashCode} method.
*
* @return suitable integer hash code
*/
@Override
public int hashCode() {
if (fast) {
int h = 0;
for (final Map.Entry<K, V> e : map.entrySet()) {
h += e.hashCode();
}
return h;
}
synchronized (map) {
int h = 0;
for (final Map.Entry<K, V> e : map.entrySet()) {
h += e.hashCode();
}
return h;
}
}
/**
* Return a shallow copy of this {@code FastHashMap} instance.
* The keys and values themselves are not copied.
*
* @return a clone of this map
*/
@Override
public Object clone() {
WeakFastHashMap<K, V> results = null;
if (fast) {
results = new WeakFastHashMap<>(map);
} else {
synchronized (map) {
results = new WeakFastHashMap<>(map);
}
}
results.setFast(getFast());
return results;
}
// Map views
/**
* Return a collection view of the mappings contained in this map. Each
* element in the returned collection is a {@code Map.Entry}.
* @return the set of map Map entries
*/
@Override
public Set<Map.Entry<K, V>> entrySet() {
return new EntrySet();
}
/**
* Return a set view of the keys contained in this map.
* @return the set of the Map's keys
*/
@Override
public Set<K> keySet() {
return new KeySet();
}
/**
* Return a collection view of the values contained in this map.
* @return the set of the Map's values
*/
@Override
public Collection<V> values() {
return new Values();
}
// Abstractions on Map creations (for subclasses such as WeakFastHashMap)
protected Map<K, V> createMap() {
return new WeakHashMap<>();
}
protected Map<K, V> createMap(final int capacity) {
return new WeakHashMap<>(capacity);
}
protected Map<K, V> createMap(final int capacity, final float factor) {
return new WeakHashMap<>(capacity, factor);
}
protected Map<K, V> createMap(final Map<? extends K, ? extends V> map) {
return new WeakHashMap<>(map);
}
protected Map<K, V> cloneMap(final Map<? extends K, ? extends V> map) {
return createMap(map);
}
// Map view inner classes
/**
* Abstract collection implementation shared by keySet(), values() and \
entrySet().
*
* @param <E> the element type
*/
private abstract class CollectionView<E> implements Collection<E> {
public CollectionView() {
}
protected abstract Collection<E> get(Map<K, V> map);
protected abstract E iteratorNext(Map.Entry<K, V> entry);
@Override
public void clear() {
if (fast) {
synchronized (WeakFastHashMap.this) {
map = createMap();
}
} else {
synchronized (map) {
get(map).clear();
}
}
}
@Override
public boolean remove(final Object o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).remove(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).remove(o);
}
}
@Override
public boolean removeAll(final Collection<?> o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).removeAll(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).removeAll(o);
}
}
@Override
public boolean retainAll(final Collection<?> o) {
if (fast) {
synchronized (WeakFastHashMap.this) {
final Map<K, V> temp = cloneMap(map);
final boolean r = get(temp).retainAll(o);
map = temp;
return r;
}
}
synchronized (map) {
return get(map).retainAll(o);
}
}
@Override
public int size() {
if (fast) {
return get(map).size();
}
synchronized (map) {
return get(map).size();
}
}
@Override
public boolean isEmpty() {
if (fast) {
return get(map).isEmpty();
}
synchronized (map) {
return get(map).isEmpty();
}
}
@Override
public boolean contains(final Object o) {
if (fast) {
return get(map).contains(o);
}
synchronized (map) {
return get(map).contains(o);
}
}
@Override
public boolean containsAll(final Collection<?> o) {
if (fast) {
return get(map).containsAll(o);
}
synchronized (map) {
return get(map).containsAll(o);
}
}
@Override
public <T> T[] toArray(final T[] o) {
if (fast) {
return get(map).toArray(o);
}
synchronized (map) {
return get(map).toArray(o);
}
}
@Override
public Object[] toArray() {
if (fast) {
return get(map).toArray();
}
synchronized (map) {
return get(map).toArray();
}
}
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (fast) {
return get(map).equals(o);
}
synchronized (map) {
return get(map).equals(o);
}
}
@Override
public int hashCode() {
if (fast) {
return get(map).hashCode();
}
synchronized (map) {
return get(map).hashCode();
}
}
@Override
public boolean add(final E o) {
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(final Collection<? extends E> c) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<E> iterator() {
return new CollectionViewIterator();
}
private class CollectionViewIterator implements Iterator<E> {
private Map<K, V> expected;
private Map.Entry<K, V> lastReturned = null;
private final Iterator<Map.Entry<K, V>> iterator;
public CollectionViewIterator() {
this.expected = map;
this.iterator = expected.entrySet().iterator();
}
@Override
public boolean hasNext() {
if (expected != map) {
throw new ConcurrentModificationException();
}
return iterator.hasNext();
}
@Override
public E next() {
if (expected != map) {
throw new ConcurrentModificationException();
}
lastReturned = iterator.next();
return iteratorNext(lastReturned);
}
@Override
public void remove() {
if (lastReturned == null) {
throw new IllegalStateException();
}
if (fast) {
synchronized (WeakFastHashMap.this) {
if (expected != map) {
throw new ConcurrentModificationException();
}
WeakFastHashMap.this.remove(lastReturned.getKey());
lastReturned = null;
expected = map;
}
} else {
iterator.remove();
lastReturned = null;
}
}
}
}
/**
* Set implementation over the keys of the FastHashMap
*/
private class KeySet extends CollectionView<K> implements Set<K> {
@Override
protected Collection<K> get(final Map<K, V> map) {
return map.keySet();
}
@Override
protected K iteratorNext(final Map.Entry<K, V> entry) {
return entry.getKey();
}
}
/**
* Collection implementation over the values of the FastHashMap
*/
private class Values extends CollectionView<V> {
@Override
protected Collection<V> get(final Map<K, V> map) {
return map.values();
}
@Override
protected V iteratorNext(final Map.Entry<K, V> entry) {
return entry.getValue();
}
}
/**
* Set implementation over the entries of the FastHashMap
*/
private class EntrySet extends CollectionView<Map.Entry<K, V>> implements \
Set<Map.Entry<K, V>> {
@Override
protected Collection<Map.Entry<K, V>> get(final Map<K, V> map) {
return map.entrySet();
}
@Override
protected Map.Entry<K, V> iteratorNext(final Map.Entry<K, V> entry) {
return entry;
}
}
}
{code}
and, Jprofiler Call Tree:
h2. Call Tree
> *Session:*|WeakFastHashMapTest|
> *Time of export:*|Sunday, May 31, 2020 2:56:14 PM CST|
> *JVM time:*|12:23|
> > >
> *View mode:* |Tree|
> *Thread selection:* |<img "height="16" width="16" border="0" hspace="0" vspace="0" \
> src=" jprofiler_images/selector_group_16.png"> All thread groups|
> *Thread status:* |<img "height="6" width="16" border="0" hspace="0" vspace="0" \
> src=" jprofiler_images/ff00c400_bff000000.png"> Runnable|
> *Aggregation level:* |Methods|
----
> !jprofiler_images/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=49,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|width=1,height=7,vspace=2! \
> 100.0% - 743 s - 1 inv. WeakFastHashMapTest.main|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 157,464 inv. org.apache.commons.beanutils2.WeakFastHashMap.put|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_te \
> e_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 107,464 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.cloneMap|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_ba \
> r_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_minus_18.g \
> if|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> 96.6% - 718 s - 107,464 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_ba \
> r_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/pixel_transparent_1.gi \
> f|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_18.gif|width=18,h \
> eight=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|width=48,height=7,vspace=2! 96.6% - 718 s \
> - 107,464 inv. java.util.WeakHashMap.<init>|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_co \
> rner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 80,342 µs - \
> 157,463 inv. java.util.Map.put|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.5% - 3,625 ms - \
> 10,654,869 inv. org.apache.commons.beanutils2.WeakFastHashMap.get|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_co \
> rner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.3% - 2,263 ms - \
> 10,654,869 inv. java.util.Map.get|
> !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_imag \
> es/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 1 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.setFast|
> !jprofiler_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 2,776 µs - 1 \
> inv. WeakFastHashMapTest.<clinit>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 51 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.<init>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 36 µs - 1 inv. \
> org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_c \
> orner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 7 µs - 1 inv. \
> java.util.WeakHashMap.<init>|
> !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofi \
> ler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler_ima \
> ges/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 3 µs - 1 inv. \
> java.util.HashMap.<init>|
96.6% of the total running time is at WeakHashMap.put
And notice that only 1/100 chance we write, other 99/100 chance we read.
If you want the data about 1/1000 or other, I will be happy to run/provide.
> use ConcurrentHashMap insteadof WeakFastHashMap
> -----------------------------------------------
>
> Key: BEANUTILS-539
> URL: https://issues.apache.org/jira/browse/BEANUTILS-539
> Project: Commons BeanUtils
> Issue Type: Improvement
> Reporter: JIN XU
> Priority: Minor
>
> Hi.
> throughtout my performance test (using Jprofiler), I found out WeakHashMap is far \
> slower than ConcurrentHashMap. Should we use ConcurrentHashMap instead of \
> WeakHashMap? I will attach my test codes here.
> {code:java}
> /*
> * Licensed to the Apache Software Foundation (ASF) under one or more
> * contributor license agreements. See the NOTICE file distributed with
> * this work for additional information regarding copyright ownership.
> * The ASF licenses this file to You 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.apache.commons.beanutils2;
> import java.util.Collection;
> import java.util.ConcurrentModificationException;
> import java.util.HashMap;
> import java.util.Iterator;
> import java.util.Map;
> import java.util.Set;
> import java.util.WeakHashMap;
> /**
> * <p>A customized implementation of {@code java.util.HashMap} designed
> * to operate in a multi-threaded environment where the large majority of
> * method calls are read-only, instead of structural changes. When operating
> * in "fast" mode, read calls are non-synchronized and write calls perform the
> * following steps:</p>
> * <ul>
> * <li>Clone the existing collection
> * <li>Perform the modification on the clone
> * <li>Replace the existing collection with the (modified) clone
> * </ul>
> * <p>When first created, objects of this class default to "slow" mode, where
> * all accesses of any type are synchronized but no cloning takes place. This
> * is appropriate for initially populating the collection, followed by a switch
> * to "fast" mode (by calling {@code setFast(true)}) after initialization
> * is complete.</p>
> *
> * <p><strong>NOTE</strong>: If you are creating and accessing a
> * {@code HashMap} only within a single thread, you should use
> * {@code java.util.HashMap} directly (with no synchronization), for
> * maximum performance.</p>
> *
> * <p><strong>NOTE</strong>: <i>This class is not cross-platform.
> * Using it may cause unexpected failures on some architectures.</i>
> * It suffers from the same problems as the double-checked locking idiom.
> * In particular, the instruction that clones the internal collection and the
> * instruction that sets the internal reference to the clone can be executed
> * or perceived out-of-order. This means that any read operation might fail
> * unexpectedly, as it may be reading the state of the internal collection
> * before the internal collection is fully formed.
> * For more information on the double-checked locking idiom, see the
> * <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html">
> * Double-Checked Locking Idiom Is Broken Declaration</a>.</p>
> *
> * @since Commons Collections 1.0
> */
> public class WeakFastHashMap<K, V> extends HashMap<K, V> {
> private static final long serialVersionUID = 1L;
> /**
> * The underlying map we are managing.
> */
> private volatile Map<K, V> map = null;
> /**
> * Are we currently operating in "fast" mode?
> */
> private boolean fast = false;
> // Constructors
> /**
> * Construct an empty map.
> */
> public WeakFastHashMap() {
> super();
> this.map = createMap();
> }
> /**
> * Construct an empty map with the specified capacity.
> *
> * @param capacity the initial capacity of the empty map
> */
> public WeakFastHashMap(final int capacity) {
> super();
> this.map = createMap(capacity);
> }
> /**
> * Construct an empty map with the specified capacity and load factor.
> *
> * @param capacity the initial capacity of the empty map
> * @param factor the load factor of the new map
> */
> public WeakFastHashMap(final int capacity, final float factor) {
> super();
> this.map = createMap(capacity, factor);
> }
> /**
> * Construct a new map with the same mappings as the specified map.
> *
> * @param map the map whose mappings are to be copied
> */
> public WeakFastHashMap(final Map<? extends K, ? extends V> map) {
> super();
> this.map = createMap(map);
> }
> // Property access
> /**
> * Returns true if this map is operating in fast mode.
> *
> * @return true if this map is operating in fast mode
> */
> public boolean getFast() {
> return this.fast;
> }
> /**
> * Sets whether this map is operating in fast mode.
> *
> * @param fast true if this map should operate in fast mode
> */
> public void setFast(final boolean fast) {
> this.fast = fast;
> }
> // Map access
> // These methods can forward straight to the wrapped Map in 'fast' mode.
> // (because they are query methods)
> /**
> * Return the value to which this map maps the specified key. Returns
> * {@code null} if the map contains no mapping for this key, or if
> * there is a mapping with a value of {@code null}. Use the
> * {@code containsKey()} method to disambiguate these cases.
> *
> * @param key the key whose value is to be returned
> * @return the value mapped to that key, or null
> */
> @Override
> public V get(final Object key) {
> if (fast) {
> return map.get(key);
> }
> synchronized (map) {
> return map.get(key);
> }
> }
> /**
> * Return the number of key-value mappings in this map.
> *
> * @return the current size of the map
> */
> @Override
> public int size() {
> if (fast) {
> return map.size();
> }
> synchronized (map) {
> return map.size();
> }
> }
> /**
> * Return {@code true} if this map contains no mappings.
> *
> * @return is the map currently empty
> */
> @Override
> public boolean isEmpty() {
> if (fast) {
> return map.isEmpty();
> }
> synchronized (map) {
> return map.isEmpty();
> }
> }
> /**
> * Return {@code true} if this map contains a mapping for the
> * specified key.
> *
> * @param key the key to be searched for
> * @return true if the map contains the key
> */
> @Override
> public boolean containsKey(final Object key) {
> if (fast) {
> return map.containsKey(key);
> }
> synchronized (map) {
> return map.containsKey(key);
> }
> }
> /**
> * Return {@code true} if this map contains one or more keys mapping
> * to the specified value.
> *
> * @param value the value to be searched for
> * @return true if the map contains the value
> */
> @Override
> public boolean containsValue(final Object value) {
> if (fast) {
> return map.containsValue(value);
> }
> synchronized (map) {
> return map.containsValue(value);
> }
> }
> // Map modification
> // These methods perform special behavior in 'fast' mode.
> // The map is cloned, updated and then assigned back.
> // See the comments at the top as to why this won't always work.
> /**
> * Associate the specified value with the specified key in this map.
> * If the map previously contained a mapping for this key, the old
> * value is replaced and returned.
> *
> * @param key the key with which the value is to be associated
> * @param value the value to be associated with this key
> * @return the value previously mapped to the key, or null
> */
> @Override
> public V put(final K key, final V value) {
> if (fast) {
> synchronized (this) {
> final Map<K, V> temp = cloneMap(map);
> final V result = temp.put(key, value);
> map = temp;
> return result;
> }
> }
> synchronized (map) {
> return map.put(key, value);
> }
> }
> /**
> * Copy all of the mappings from the specified map to this one, replacing
> * any mappings with the same keys.
> *
> * @param in the map whose mappings are to be copied
> */
> @Override
> public void putAll(final Map<? extends K, ? extends V> in) {
> if (fast) {
> synchronized (this) {
> final Map<K, V> temp = cloneMap(map);
> temp.putAll(in);
> map = temp;
> }
> } else {
> synchronized (map) {
> map.putAll(in);
> }
> }
> }
> /**
> * Remove any mapping for this key, and return any previously
> * mapped value.
> *
> * @param key the key whose mapping is to be removed
> * @return the value removed, or null
> */
> @Override
> public V remove(final Object key) {
> if (fast) {
> synchronized (this) {
> final Map<K, V> temp = cloneMap(map);
> final V result = temp.remove(key);
> map = temp;
> return result;
> }
> }
> synchronized (map) {
> return map.remove(key);
> }
> }
> /**
> * Remove all mappings from this map.
> */
> @Override
> public void clear() {
> if (fast) {
> synchronized (this) {
> map = createMap();
> }
> } else {
> synchronized (map) {
> map.clear();
> }
> }
> }
> // Basic object methods
> /**
> * Compare the specified object with this list for equality. This
> * implementation uses exactly the code that is used to define the
> * list equals function in the documentation for the
> * {@code Map.equals} method.
> *
> * @param o the object to be compared to this list
> * @return true if the two maps are equal
> */
> @Override
> public boolean equals(final Object o) {
> // Simple tests that require no synchronization
> if (o == this) {
> return true;
> } else if (!(o instanceof Map)) {
> return false;
> }
> final Map<?, ?> mo = (Map<?, ?>) o;
> // Compare the two maps for equality
> if (fast) {
> if (mo.size() != map.size()) {
> return false;
> }
> for (final Map.Entry<K, V> e : map.entrySet()) {
> final K key = e.getKey();
> final V value = e.getValue();
> if (value == null) {
> if (!(mo.get(key) == null && mo.containsKey(key))) {
> return false;
> }
> } else {
> if (!value.equals(mo.get(key))) {
> return false;
> }
> }
> }
> return true;
> }
> synchronized (map) {
> if (mo.size() != map.size()) {
> return false;
> }
> for (final Map.Entry<K, V> e : map.entrySet()) {
> final K key = e.getKey();
> final V value = e.getValue();
> if (value == null) {
> if (!(mo.get(key) == null && mo.containsKey(key))) {
> return false;
> }
> } else {
> if (!value.equals(mo.get(key))) {
> return false;
> }
> }
> }
> return true;
> }
> }
> /**
> * Return the hash code value for this map. This implementation uses
> * exactly the code that is used to define the list hash function in the
> * documentation for the {@code Map.hashCode} method.
> *
> * @return suitable integer hash code
> */
> @Override
> public int hashCode() {
> if (fast) {
> int h = 0;
> for (final Map.Entry<K, V> e : map.entrySet()) {
> h += e.hashCode();
> }
> return h;
> }
> synchronized (map) {
> int h = 0;
> for (final Map.Entry<K, V> e : map.entrySet()) {
> h += e.hashCode();
> }
> return h;
> }
> }
> /**
> * Return a shallow copy of this {@code FastHashMap} instance.
> * The keys and values themselves are not copied.
> *
> * @return a clone of this map
> */
> @Override
> public Object clone() {
> WeakFastHashMap<K, V> results = null;
> if (fast) {
> results = new WeakFastHashMap<>(map);
> } else {
> synchronized (map) {
> results = new WeakFastHashMap<>(map);
> }
> }
> results.setFast(getFast());
> return results;
> }
> // Map views
> /**
> * Return a collection view of the mappings contained in this map. Each
> * element in the returned collection is a {@code Map.Entry}.
> * @return the set of map Map entries
> */
> @Override
> public Set<Map.Entry<K, V>> entrySet() {
> return new EntrySet();
> }
> /**
> * Return a set view of the keys contained in this map.
> * @return the set of the Map's keys
> */
> @Override
> public Set<K> keySet() {
> return new KeySet();
> }
> /**
> * Return a collection view of the values contained in this map.
> * @return the set of the Map's values
> */
> @Override
> public Collection<V> values() {
> return new Values();
> }
> // Abstractions on Map creations (for subclasses such as WeakFastHashMap)
> protected Map<K, V> createMap() {
> return new WeakHashMap<>();
> }
> protected Map<K, V> createMap(final int capacity) {
> return new WeakHashMap<>(capacity);
> }
> protected Map<K, V> createMap(final int capacity, final float factor) {
> return new WeakHashMap<>(capacity, factor);
> }
> protected Map<K, V> createMap(final Map<? extends K, ? extends V> map) {
> return new WeakHashMap<>(map);
> }
> protected Map<K, V> cloneMap(final Map<? extends K, ? extends V> map) {
> return createMap(map);
> }
> // Map view inner classes
> /**
> * Abstract collection implementation shared by keySet(), values() and entrySet().
> *
> * @param <E> the element type
> */
> private abstract class CollectionView<E> implements Collection<E> {
> public CollectionView() {
> }
> protected abstract Collection<E> get(Map<K, V> map);
> protected abstract E iteratorNext(Map.Entry<K, V> entry);
> @Override
> public void clear() {
> if (fast) {
> synchronized (WeakFastHashMap.this) {
> map = createMap();
> }
> } else {
> synchronized (map) {
> get(map).clear();
> }
> }
> }
> @Override
> public boolean remove(final Object o) {
> if (fast) {
> synchronized (WeakFastHashMap.this) {
> final Map<K, V> temp = cloneMap(map);
> final boolean r = get(temp).remove(o);
> map = temp;
> return r;
> }
> }
> synchronized (map) {
> return get(map).remove(o);
> }
> }
> @Override
> public boolean removeAll(final Collection<?> o) {
> if (fast) {
> synchronized (WeakFastHashMap.this) {
> final Map<K, V> temp = cloneMap(map);
> final boolean r = get(temp).removeAll(o);
> map = temp;
> return r;
> }
> }
> synchronized (map) {
> return get(map).removeAll(o);
> }
> }
> @Override
> public boolean retainAll(final Collection<?> o) {
> if (fast) {
> synchronized (WeakFastHashMap.this) {
> final Map<K, V> temp = cloneMap(map);
> final boolean r = get(temp).retainAll(o);
> map = temp;
> return r;
> }
> }
> synchronized (map) {
> return get(map).retainAll(o);
> }
> }
> @Override
> public int size() {
> if (fast) {
> return get(map).size();
> }
> synchronized (map) {
> return get(map).size();
> }
> }
> @Override
> public boolean isEmpty() {
> if (fast) {
> return get(map).isEmpty();
> }
> synchronized (map) {
> return get(map).isEmpty();
> }
> }
> @Override
> public boolean contains(final Object o) {
> if (fast) {
> return get(map).contains(o);
> }
> synchronized (map) {
> return get(map).contains(o);
> }
> }
> @Override
> public boolean containsAll(final Collection<?> o) {
> if (fast) {
> return get(map).containsAll(o);
> }
> synchronized (map) {
> return get(map).containsAll(o);
> }
> }
> @Override
> public <T> T[] toArray(final T[] o) {
> if (fast) {
> return get(map).toArray(o);
> }
> synchronized (map) {
> return get(map).toArray(o);
> }
> }
> @Override
> public Object[] toArray() {
> if (fast) {
> return get(map).toArray();
> }
> synchronized (map) {
> return get(map).toArray();
> }
> }
> @Override
> public boolean equals(final Object o) {
> if (o == this) {
> return true;
> }
> if (fast) {
> return get(map).equals(o);
> }
> synchronized (map) {
> return get(map).equals(o);
> }
> }
> @Override
> public int hashCode() {
> if (fast) {
> return get(map).hashCode();
> }
> synchronized (map) {
> return get(map).hashCode();
> }
> }
> @Override
> public boolean add(final E o) {
> throw new UnsupportedOperationException();
> }
> @Override
> public boolean addAll(final Collection<? extends E> c) {
> throw new UnsupportedOperationException();
> }
> @Override
> public Iterator<E> iterator() {
> return new CollectionViewIterator();
> }
> private class CollectionViewIterator implements Iterator<E> {
> private Map<K, V> expected;
> private Map.Entry<K, V> lastReturned = null;
> private final Iterator<Map.Entry<K, V>> iterator;
> public CollectionViewIterator() {
> this.expected = map;
> this.iterator = expected.entrySet().iterator();
> }
> @Override
> public boolean hasNext() {
> if (expected != map) {
> throw new ConcurrentModificationException();
> }
> return iterator.hasNext();
> }
> @Override
> public E next() {
> if (expected != map) {
> throw new ConcurrentModificationException();
> }
> lastReturned = iterator.next();
> return iteratorNext(lastReturned);
> }
> @Override
> public void remove() {
> if (lastReturned == null) {
> throw new IllegalStateException();
> }
> if (fast) {
> synchronized (WeakFastHashMap.this) {
> if (expected != map) {
> throw new ConcurrentModificationException();
> }
> WeakFastHashMap.this.remove(lastReturned.getKey());
> lastReturned = null;
> expected = map;
> }
> } else {
> iterator.remove();
> lastReturned = null;
> }
> }
> }
> }
> /**
> * Set implementation over the keys of the FastHashMap
> */
> private class KeySet extends CollectionView<K> implements Set<K> {
> @Override
> protected Collection<K> get(final Map<K, V> map) {
> return map.keySet();
> }
> @Override
> protected K iteratorNext(final Map.Entry<K, V> entry) {
> return entry.getKey();
> }
> }
> /**
> * Collection implementation over the values of the FastHashMap
> */
> private class Values extends CollectionView<V> {
> @Override
> protected Collection<V> get(final Map<K, V> map) {
> return map.values();
> }
> @Override
> protected V iteratorNext(final Map.Entry<K, V> entry) {
> return entry.getValue();
> }
> }
> /**
> * Set implementation over the entries of the FastHashMap
> */
> private class EntrySet extends CollectionView<Map.Entry<K, V>> implements \
> Set<Map.Entry<K, V>> { @Override
> protected Collection<Map.Entry<K, V>> get(final Map<K, V> map) {
> return map.entrySet();
> }
> @Override
> protected Map.Entry<K, V> iteratorNext(final Map.Entry<K, V> entry) {
> return entry;
> }
> }
> }
> {code}
> and, Jprofiler Call Tree:
> h2. Call Tree
> > *Session:*|WeakFastHashMapTest|
> > *Time of export:*|Sunday, May 31, 2020 2:56:14 PM CST|
> > *JVM time:*|12:23|
> > > >
> > *View mode:* |Tree|
> > *Thread selection:* |<img "height="16" width="16" border="0" hspace="0" \
> > vspace="0" src=" jprofiler_images/selector_group_16.png"> All thread groups|
> > *Thread status:* |<img "height="6" width="16" border="0" hspace="0" vspace="0" \
> > src=" jprofiler_images/ff00c400_bff000000.png"> Runnable|
> > *Aggregation level:* |Methods|
> ----
>
> > !jprofiler_images/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ff990000.png|width=49,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|width=1,height=7,vspace=2! \
> > 100.0% - 743 s - 1 inv. WeakFastHashMapTest.main|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> > 96.6% - 718 s - 157,464 inv. org.apache.commons.beanutils2.WeakFastHashMap.put|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/men \
> > u_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> > 96.6% - 718 s - 107,464 inv. \
> > org.apache.commons.beanutils2.WeakFastHashMap.cloneMap|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/men \
> > u_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_minu \
> > s_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ff990000.png|width=48,height=7,vspace=2!!jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! \
> > 96.6% - 718 s - 107,464 inv. \
> > org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/men \
> > u_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/pixel_transparen \
> > t_1.gif|width=18,height=18,align=left!!jprofiler_images/tree/menu_corner_18.gif|wi \
> > dth=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|width=48,height=7,vspace=2! 96.6% - 718 \
> > s - 107,464 inv. java.util.WeakHashMap.<init>|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/men \
> > u_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 80,342 µs - \
> > 157,463 inv. java.util.Map.put|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.5% - 3,625 ms - \
> > 10,654,869 inv. org.apache.commons.beanutils2.WeakFastHashMap.get|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/men \
> > u_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.3% - 2,263 ms - \
> > 10,654,869 inv. java.util.Map.get|
> > !jprofiler_images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_im \
> > ages/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 1 µs - 1 inv. \
> > org.apache.commons.beanutils2.WeakFastHashMap.setFast|
> > !jprofiler_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jpr \
> > ofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 2,776 µs - 1 \
> > inv. WeakFastHashMapTest.<clinit>|
> > !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jpro \
> > filer_images/tree/menu_corner_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 51 µs - 1 \
> > inv. org.apache.commons.beanutils2.WeakFastHashMap.<init>|
> > !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jpro \
> > filer_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler \
> > _images/tree/menu_tee_minus_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 36 µs - 1 \
> > inv. org.apache.commons.beanutils2.WeakFastHashMap.createMap|
> > !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jpro \
> > filer_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler \
> > _images/tree/menu_bar_18.gif|width=18,height=18,align=left!!jprofiler_images/tree/ \
> > menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 7 µs - 1 inv. \
> > java.util.WeakHashMap.<init>|
> > !jprofiler_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jpro \
> > filer_images/tree/pixel_transparent_1.gif|width=18,height=18,align=left!!jprofiler \
> > _images/tree/menu_corner_18.gif|width=18,height=18,align=left!!jprofiler_images/call_method_16_filter_underlay_16.png|width=16,height=16,align=left! \
> > !jprofiler_images/pixel_ffff3300.png|height=7,vspace=2! 0.0% - 3 µs - 1 inv. \
> > java.util.HashMap.<init>|
>
> 96.6% of the total running time is at WeakHashMap.put
> And notice that only 1/100 chance we write, other 99/100 chance we read.
> If you want the data about 1/1000 or other, I will be happy to run/provide.
> [https://github.com/apache/commons-beanutils/pull/27]
>
--
This message was sent by Atlassian Jira
(v8.3.4#803005)
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic