The built-in
Map
implementations, such as HashMap
and ConcurrentHashMap
are fast but they must be initialized with mappings before they can be used for looking up values. Also, they are limited in size by practical means such as heap and RAM size. Lastly, they are local to the JVM it runs in.The initialization process can slow down critical startup for microservices, especially when reading mappings from a remote REST interface or a remote database. In this article, you will learn how you can start your microservice applications in seconds instead of minutes by using memory-mapped
ChronicleMap
instances and how Maps can be shared between JVMs in this third article in an article series about CronicleMap.Read more about the fundamentals of CronicleMap in the first article.
Read more about file mapped CronicleMap objects in the second article.
Creating a Shared Map
As described in the second article in the series, we can easily create a file mapped Map like this:private static Map<Long, Point> createFileMapped() {Created
try {
return ChronicleMap
.of(Long.class, Point.class)
.averageValueSize(8)
.valueMarshaller(PointSerializer.getInstance())
.entries(10_000_000)
.createPersistedTo(new File("my-map"));
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
Map
objects can now be accessed by any JVM that has access to the “my-map” file. Updates to the maps will be shared among the participating JVMs via the shared file.Initializing the Map
As also shown in the second article, we could create and initialize aMap
like this:final Map<Long, Point> m3 = LongStream.range(0, 10_000_000)When running on my laptop (MacBook Pro mid 2015, 16 GB, 2.2 GHz Intel Core i7), it takes about 10 seconds to create and fill the
.boxed()
.collect(
toMap(
Function.identity(),
FillMaps::pointFrom,
(u, v) -> {
throw new IllegalStateException();
},
FillMaps::createFileMapped
)
);
Map
with 10 million entries.If the
Map
contents were retrieved externally (as opposed to being created locally by the pointFrom()
method), it would likely take much longer time to fill the Map
. For example, if we get 50 Mbit/s REST throughput and each JSON Point representation consumes 25 bytes, then it would take some 60 seconds to fill the Map
.Starting a new JVM
Now that there is a pre-existing mapped file, we can start directly off this file as shown in this snippet:return ChronicleMapThis will create a
.of(Long.class, Point.class)
.averageValueSize(8)
.valueMarshaller(PointSerializer.getInstance())
.entries(10_000_000)
.createOrRecoverPersistedTo(new File("my-map"));
Map
directly from the existing “my-map” file.Running this on my laptop will yield a start time of 5 seconds. This could be compared to the 60 second REST example, yielding a 90% startup time reduction.
Running Several JVMs on the Same Node
We could elect to run several JVMs on the same physical server node. By doing so, we benefit from the OS’es ability to make mappings of the file available for each JVM by exposing shared memory. This constitutes an efficient and low latency means of communication between the JVMs. The fact that there is a common pool of mapped memory makes the memory management much more efficient compared to a situation where each and every JVM/OS would have to maintain its own separate mappings.Summary
ChronicleMaps can be shared between participating JVM via shared filesStartup times can be reduced significantly using shared files
If JVMs are running on the same physical machine, performance and efficiency is further improved
Shared files via ChronicleMap provides a low latency means of communication between JVMs