diff --git a/README.md b/README.md index ebb5dac72..e4546b46e 100644 --- a/README.md +++ b/README.md @@ -316,8 +316,11 @@ supports the following methods : - [`V getUsing(K key, V value);`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMap.html#getUsing-K-V-) - [`V acquireUsing(K key, V value);`](http://openhft.github.io/Chronicle-Map/apidocs/net/openhft/chronicle/map/ChronicleMap.html#acquireUsing-K-V-) + - ReadContext getUsingLocked(@NotNull K key, @NotNull V usingValue); + - WriteContext acquireUsingLocked(@NotNull K key, @NotNull V usingValue); -These methods let you provide the object which the data will be written to, even if the object it + +These methods let you provide the object which the data will be written to, even if the object is immutable. For example ``` java @@ -335,9 +338,9 @@ Exactly like `map.getUsing()`, `map.acquireUsing()` will give you back a referen based on a key, but unlike `map.getUsing()` if there is not an entry in the map for this key the entry will be added and the value return will we the same value which you provided. ( The section below - covers `map.getUsing()`, `map.acquireUsing()` in greater details ) + covers both `map.getUsing()` and `map.acquireUsing()` in more detail ) -#### Off Heap and How to Improve Performance +#### Improve Performance using and Proxy Object to off heap memory Chronicle Map stores its data off heap. There are some distinct advantages in using off heap data storage @@ -346,7 +349,8 @@ Chronicle Map stores its data off heap. There are some distinct advantages in us * Your Chronicle map and your data contained within it is serialised so it can be stored in off heap memory. When this memory is shared between processes on the same server, the chronicle map is able to distribute entries between processes with extremely low overhead, -as both processes are sharing the same off heap memory space. Since your objects are already stored off heap ( as a series of bytes ), replication you entries over the network add relatively low over head. +as both processes are sharing the same off heap memory space. Since your objects are already stored off +heap ( as a series of bytes ), replicating entries over the network add relatively low over head. One of the downsides of an Off Heap Map is that whenever you wish to get a value ( on heap ) from an entry ``` java @@ -355,17 +359,17 @@ Value v= get(key) that entry has to be deserialised onto the java heap so that you can use its value just like any other java object. So if you were to call get(key) ten times, ``` java for(int i=1;i<=10;i++) { - Value v= get(key) + Value v = get(key) } ``` -this would create 10 separate copies of the value. As each time get() is called the map has to +this would create 10 separate instances of the value. As each time get() is called the map has to first create a Object to store the result in and then deserialise the value stored in the off heap entry. If you want to get the value back on heap so that you can use it like a normal java value, There is nothing we can do about the deserialisation as this has to occur every time, ( since the value may have been changed by another thread ), but we don’t have to create the object each time. This is why we create getUsing(key,using). By reducing the number of objects you create, you reduce the amount of work that the garbage collector has to carry out, -this in turn may, improve your overall performance. So back to getUsing(key,using), If you wish to reuse and existing object ( in this case the ‘using’ value ), you can instead call +this in turn may, improve your overall performance. So back to `getUsing(key,using)`, If you wish to reuse and existing object ( in this case the ‘using’ value ), you can instead call ``` java Value using = new Value(); diff --git a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java index 99d15c952..b6adcbe8b 100644 --- a/src/main/java/net/openhft/chronicle/map/ChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/ChronicleMap.java @@ -32,19 +32,19 @@ import java.util.concurrent.ConcurrentMap; /** * Extension of {@link ConcurrentMap} interface, stores the data off-heap. * - *

For information on

see {@link - * AbstractChronicleMapBuilder} documentation. + *

For information on

see {@link AbstractChronicleMapBuilder} + * documentation. * - *

Functionally this interface defines some methods supporting garbage-free off-heap programming: - * {@link #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}. + *

Functionally this interface defines some methods supporting garbage-free off-heap programming: {@link + * #getUsing(Object, Object)}, {@link #acquireUsing(Object, Object)}. * - *

Roughly speaking, {@code ChronicleMap} compares keys and values by their binary serialized - * form, that shouldn't necessary be the same equality relation as defined by built-in {@link - * Object#equals(Object)} method, which is prescribed by general {@link Map} contract. + *

Roughly speaking, {@code ChronicleMap} compares keys and values by their binary serialized form, that + * shouldn't necessary be the same equality relation as defined by built-in {@link Object#equals(Object)} + * method, which is prescribed by general {@link Map} contract. * - *

Note that {@code ChronicleMap} extends {@link Closeable}, don't forget to {@linkplain #close() - * close} map when it is no longer needed. + *

Note that {@code ChronicleMap} extends {@link Closeable}, don't forget to {@linkplain #close() close} + * map when it is no longer needed. * * @param the map key type * @param the map value type @@ -59,41 +59,38 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { long longSize(); /** - * Returns the value to which the specified key is mapped, or {@code null} if this map contains - * no mapping for the key. + * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping + * for the key. * - *

If the value class allows reusing, particularly if it is a {@link Byteable} subclass, - * consider {@link #getUsing(Object, Object)} method instead of this to reduce garbage - * creation. + *

If the value class allows reusing, particularly if it is a {@link Byteable} subclass, consider + * {@link #getUsing(Object, Object)} method instead of this to reduce garbage creation. * * @param key the key whose associated value is to be returned - * @return the value to which the specified key is mapped after this method call, or {@code - * null} if no value is mapped + * @return the value to which the specified key is mapped after this method call, or {@code null} if no + * value is mapped * @see #getUsing(Object, Object) */ @Override V get(Object key); /** - * Returns the value to which the specified key is mapped, read to the provided {@code value} - * object, if possible, or returns {@code null}, if this map contains no mapping for the key. + * Returns the value to which the specified key is mapped, read to the provided {@code value} object, if + * possible, or returns {@code null}, if this map contains no mapping for the key. * - *

If the specified key is present in the map, the value data is read to the provided {@code - * value} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) read(Bytes, - * value)} or value reader's {@link BytesReader#read(Bytes, long, Object) read(Bytes, size, - * value)} method, depending on what deserialization strategy is configured on the builder, - * using which this map was constructed. If the value deserializer is able to reuse the given - * {@code value} object, calling this method instead of {@link #get(Object)} could help to - * reduce garbage creation. + *

If the specified key is present in the map, the value data is read to the provided {@code value} + * object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) read(Bytes, value)} or value + * reader's {@link BytesReader#read(Bytes, long, Object) read(Bytes, size, value)} method, depending on + * what deserialization strategy is configured on the builder, using which this map was constructed. If + * the value deserializer is able to reuse the given {@code value} object, calling this method instead of + * {@link #get(Object)} could help to reduce garbage creation. * *

The provided {@code value} object is allowed to be {@code null}, in this case {@code - * map.getUsing(key, null)} call is semantically equivalent to simple {@code map.get(key)} - * call. + * map.getUsing(key, null)} call is semantically equivalent to simple {@code map.get(key)} call. * * @param key the key whose associated value is to be returned * @param usingValue the object to read value data in, if possible - * @return the value to which the specified key is mapped, or {@code null} if this map contains - * no mapping for the key + * @return the value to which the specified key is mapped, or {@code null} if this map contains no mapping + * for the key * @see #get(Object) * @see #acquireUsing(Object, Object) * @see ChronicleMapBuilder#valueMarshaller(BytesMarshaller) @@ -101,39 +98,36 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { V getUsing(K key, V usingValue); /** - * The method is similar {@link V getUsing(K key, V usingValue);} but in addition read locks - * the map segment to operations to be performed atomically. ( see the example below ) + * The method is similar {@link V getUsing(K key, V usingValue);} but in addition read locks the map + * segment to operations to be performed atomically. ( see the example below ) * - * Returns the ReadContext which holds a map segment lock and provides method to get the value - * ( to which the specified key is mapped) atomically ( see example below for an explanation ), - * read to the provided {@code value} object, if possible, or returns {@code null}, if this map - * contains no mapping for the key. + * Returns the ReadContext which holds a map segment lock and provides method to get the value ( to which + * the specified key is mapped) atomically ( see example below for an explanation ), read to the provided + * {@code value} object, if possible, or returns {@code null}, if this map contains no mapping for the + * key. * - *

If the specified key is present in the map, the readContext.value() uses the - * provided {@code usingValue} object via value marshaller's {@link BytesMarshaller#read(Bytes, - * Object) read(Bytes, value)} or value reader's {@link BytesReader#read(Bytes, long, Object) - * read(Bytes, size, value)} method, depending on what deserialization strategy is configured on - * the builder, using which this map was constructed. If the value deserializer is able to reuse - * the given {@code usingValue} object, calling this method instead of {@link #get(Object)} - * could help to reduce garbage creation. + *

If the specified key is present in the map, the readContext.value() uses the provided {@code + * usingValue} object via value marshaller's {@link BytesMarshaller#read(Bytes, Object) read(Bytes, + * value)} or value reader's {@link BytesReader#read(Bytes, long, Object) read(Bytes, size, value)} + * method, depending on what deserialization strategy is configured on the builder, using which this map + * was constructed. If the value deserializer is able to reuse the given {@code usingValue} object, + * calling this method instead of {@link #get(Object)} could help to reduce garbage creation. * *

try (ReadContext>?, BondVOInterface< context = map.getUsingLocked(key,using)) { * BondVOInterface bond = context.value().getValue(); * * long issueDate = bond.getIssueDate(); String symbol = bond.getSymbol(); * - * // add your logic here ( the lock will ensure this bond can not be changed by another thread - * ) + * // add your logic here ( the lock will ensure this bond can not be changed by another thread ) * * } // the read lock is released here

* - * To ensure that you can read the 'issueDate' and 'symbol' can be read atomically, these values - * must be read while the segment lock is in place. + * To ensure that you can read the 'issueDate' and 'symbol' can be read atomically, these values must be + * read while the segment lock is in place. * * *

The provided {@code value} object is allowed to be {@code null}, in this case * - * @param key the key whose associated value is to be returned * @param usingValue the object to read value data in, if possible * @return the read context containing the value to which the specified key is mapped @@ -151,14 +145,13 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash { * *

If the specified key if absent in the map, {@linkplain AbstractChronicleMapBuilder#defaultValue(Object) * default value} is taken or {@linkplain AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) - * default value provider} is called. Then this object is put to this map for the specified - * key. + * default value provider} is called. Then this object is put to this map for the specified key. * *

Then, either if the key was initially absent in the map or already present, the value is - * deserialized just as during {@link #getUsing(Object, Object) getUsing(key, usingValue)} call, - * passed the same {@code key} and {@code usingValue} as into this method call. This means, as - * in {@link #getUsing}, {@code usingValue} could safely be {@code null}, in this case a new - * value instance is created to deserialize the data. + * deserialized just as during {@link #getUsing(Object, Object) getUsing(key, usingValue)} call, passed + * the same {@code key} and {@code usingValue} as into this method call. This means, as in {@link + * #getUsing}, {@code usingValue} could safely be {@code null}, in this case a new value instance is + * created to deserialize the data. * *

In code, {@code acquireUsing} is specified as

{@code
      * V acquireUsing(K key, V usingValue) {
@@ -167,27 +160,24 @@ public interface ChronicleMap extends ConcurrentMap, ChronicleHash {
      *     return getUsing(key, usingValue);
      * }}
Where {@code defaultValue(key)} returns either {@linkplain * AbstractChronicleMapBuilder#defaultValue(Object) default value} or {@link - * AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) - * defaultValueProvider.} + * AbstractChronicleMapBuilder#defaultValueProvider(DefaultValueProvider) defaultValueProvider.} * *

If the {@code ChronicleMap} is off-heap updatable, i. e. created via {@link - * OffHeapUpdatableChronicleMapBuilder} builder (values are {@link Byteable}), there is one more - * option of what to do if the key is absent in the map, see {@link - * OffHeapUpdatableChronicleMapBuilder#prepareValueBytesOnAcquire(PrepareValueBytes)}. By - * default, value bytes are just zeroed out, no default value, either provided for key or - * constant, is put for the absent key. + * OffHeapUpdatableChronicleMapBuilder} builder (values are {@link Byteable}), there is one more option of + * what to do if the key is absent in the map, see {@link OffHeapUpdatableChronicleMapBuilder#prepareValueBytesOnAcquire(PrepareValueBytes)}. + * By default, value bytes are just zeroed out, no default value, either provided for key or constant, is + * put for the absent key. * * @param key the key whose associated value is to be returned - * @param usingValue the object to read value data in, if possible + * @param usingValue the object to read value data in, if present. Can not be null * @return value to which the given key is mapping after this call, either found or created * @see #getUsing(Object, Object) */ - public V acquireUsing(K key, V usingValue); + V acquireUsing(@NotNull K key, @NotNull V usingValue); @NotNull WriteContext acquireUsingLocked(@NotNull K key, @NotNull V usingValue); - } diff --git a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java index 38eee0456..48ee4a387 100644 --- a/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/StatelessChronicleMap.java @@ -391,7 +391,7 @@ class StatelessChronicleMap implements ChronicleMap, Closeable { } - public synchronized V acquireUsing(K key, V usingValue) { + public synchronized V acquireUsing(@NotNull K key, @NotNull V usingValue) { throw new UnsupportedOperationException("acquireUsing() is not supported for stateless " + "clients"); } diff --git a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java index 135db017f..2d4d8cfb3 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaChronicleMap.java @@ -390,7 +390,7 @@ class VanillaChronicleMap, @Override - public V acquireUsing(K key, V usingValue) { + public V acquireUsing(@NotNull K key, @NotNull V usingValue) { try (WriteContext kvContext = lookupUsing(key, usingValue, LockType.WRITE_LOCK)) { diff --git a/src/main/java/net/openhft/chronicle/map/VanillaShortShortMultiMap.java b/src/main/java/net/openhft/chronicle/map/VanillaShortShortMultiMap.java index 2b97a209d..82670c5d6 100644 --- a/src/main/java/net/openhft/chronicle/map/VanillaShortShortMultiMap.java +++ b/src/main/java/net/openhft/chronicle/map/VanillaShortShortMultiMap.java @@ -85,6 +85,8 @@ class VanillaShortShortMultiMap implements MultiMap { * constructor as the second argument */ public static long sizeOfBitSetInBytes(long minCapacity) { + + return VanillaIntIntMultiMap.sizeOfBitSetInBytes(minCapacity); } diff --git a/src/main/java/net/openhft/chronicle/map/WriteContext.java b/src/main/java/net/openhft/chronicle/map/WriteContext.java index 26cc84baf..275341eb3 100644 --- a/src/main/java/net/openhft/chronicle/map/WriteContext.java +++ b/src/main/java/net/openhft/chronicle/map/WriteContext.java @@ -22,9 +22,6 @@ package net.openhft.chronicle.map; */ public interface WriteContext extends Context { void dontPutOnClose(); - void removeEntry(); - // todo I have no idea why we have this, as this will always be done on when calling close() - // void update(); } diff --git a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java index cb5b8f578..a11205ece 100644 --- a/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java +++ b/src/test/java/net/openhft/chronicle/map/ReplicationCheckingMap.java @@ -246,7 +246,7 @@ public class ReplicationCheckingMap implements ChronicleMap { } @Override - public V acquireUsing(K key, V usingValue) { + public V acquireUsing(@NotNull K key, @NotNull V usingValue) { return map1.acquireUsing(key, usingValue); } diff --git a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java index 61b625979..961455a08 100644 --- a/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java +++ b/src/test/java/net/openhft/chronicle/map/jrs166/map/StatelessChronicleMapTest.java @@ -151,7 +151,7 @@ public class StatelessChronicleMapTest extends JSR166TestCase { } @Override - public V acquireUsing(K key, V usingValue) { + public V acquireUsing(@NotNull K key, @NotNull V usingValue) { return d.acquireUsing(key, usingValue); }