Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Java Holiday Calendar 2016 (Day 20): Break Out of the Java Heap

DZone's Guide to

Java Holiday Calendar 2016 (Day 20): Break Out of the Java Heap

Moving data off the heap, within reason, can let you work more efficiently while keeping latency low. That way, you can avoid the garbage collection wall for longer.

· Java Zone
Free Resource

Never build auth again! The Okta Developer Platform makes it simple to implement authentication, authorization, MFA and more in Java applications. Get started with the free API.

Image title

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 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 general, 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 you can see, 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.

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:
java ,garbage collection ,java performance ,off heap ,tutorial

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}