Day 20, Java Holiday Calendar 2016, Breakout of the Java Heap

by Per Minborg

on December 20, 2016

Day 20, Java Holiday Calendar 2016, Breakout of the Java Heap




Today's tip is about storing things off heap. As we all know, Java will occasionally clean up the heap (i.e. invoke its Garbage Collector or GC for short) and remove objects that are no longer used. As the heap grows, so will the time it takes to clean it up. Eventually, the GC will take seconds or minutes and we have hit "the GC wall". Historically, this was a problem for heaps above some 10 GB of data but nowadays we can have larger heaps. How big depends on a vast number of factors.

One way of reducing GC impact is to store data off heap where the GC is not even looking. This way, we can grow to any data size without caring about the GC. The drawback is that we have to manage our memory manually and also provide a means of converting data back and forth between the two memory regions. In the general case, this is a bit tricky but if we limit ourselves to the primitive types like int, long, double and the likes, it is fairly easy.

Consider the following OffHeapIntArray:

public final class OffHeapIntArray implements Iterable<Integer> {

private final IntBuffer buffer;
private final int length;

public OffHeapIntArray(int length) {
if (length < 0) {
throw new IllegalArgumentException();
}
this.length = length;
/* Allocates memory off heap */
this.buffer = ByteBuffer.allocateDirect(length * Integer.BYTES)
.asIntBuffer();
}

public int get(int index) {
return buffer.get(index);
}

public void set(int index, int value) {
buffer.put(index, value);
}

public int length() {
return length;
}

@Override
public PrimitiveIterator.OfInt iterator() {
return new Iter();
}

@Override
public void forEach(Consumer<? super Integer> action) {
for (int i = 0; i < length; i++) {
action.accept(i);
}
}

@Override
public Spliterator.OfInt spliterator() {
return Spliterators.spliterator(iterator(), length,
Spliterator.SIZED
| Spliterator.SUBSIZED
| Spliterator.NONNULL
| Spliterator.CONCURRENT
);
}

public IntStream stream() {
return StreamSupport.intStream(spliterator(), false);
}

private final class Iter implements PrimitiveIterator.OfInt {

private int currentIndex;

public Iter() {
currentIndex = 0;
}

@Override
public int nextInt() {
if (hasNext()) {
return get(currentIndex++);
}
throw new NoSuchElementException();
}

@Override
public void forEachRemaining(IntConsumer action) {
while (currentIndex < length) {
action.accept(get(currentIndex++));
}
}

@Override
public boolean hasNext() {
return currentIndex < length;
}

@Override
public Integer next() {
return nextInt();
}

}

}

As can be seen, it stores all the int elements off heap and allows us to do a number of things like this:

    public static final void main(String... args) {
final OffHeapIntArray array = new OffHeapIntArray(10);
array.set(0, 100);
array.set(1, 101);
array.set(9, 109);

System.out.println("** Iterate **");
for (int val : array) {
System.out.println(val);
}

System.out.println("** Stream **");
array.stream()
.filter(i -> i > 0)
.forEach(System.out::println);

}

This will produce the following output:

** Iterate **
100
101
0
0
0
0
0
0
0
109
** Stream **
100
101
109

It is possible to create more advanced off heap classes like OffHeapMap, OffHeapArrayList etc. that store all data off heap in a similar way. Speedment Enterprise is using a more advanced version of this technology to be able to store large databases as in-JVM-memory objects. In fact, it is possible to store more data than the available RAM since byte buffers can be mapped onto files. This opens up the path to mammoth JVMs with terabytes of data available at ultra-low latency.


Follow the Java Holiday Calendar 2016 with small tips and tricks all the way through the winter holiday season. I am contributing to open-source Speedment, a stream based ORM tool and runtime. Please check it out on GitHub.

About

Per Minborg

Per Minborg is a Palo Alto based developer and architect, currently serving as CTO at Speedment, Inc. He is a regular speaker at various conferences e.g. JavaOne, DevNexus, Jdays, JUGs and Meetups. Per has 15+ US patent applications and invention disclosures. He is a JavaOne alumni and co-author of the publication “Modern Java”.