All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sleepycat.je.evictor.CHeapAllocator Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle Berkeley
 * DB Java Edition made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle Berkeley DB Java Edition for a copy of the
 * license and additional information.
 */

package com.sleepycat.je.evictor;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLong;

import sun.misc.Unsafe;

/**
 * The default implementation of the off-heap allocator.
 *
 * Uses the sun.misc.Unsafe class to call the native 'malloc' and 'free'
 * functions to allocate memory from the 'C' runtime heap.
 *
 * This class should not be referenced symbolically by any other other class.
 * This is necessary to avoid a linkage error if JE is run on a JVM without the
 * Unsafe class. The {@link OffHeapAllocatorFactory} loads this class by name,
 * using reflection.
 */
class CHeapAllocator implements OffHeapAllocator {

    /*
     * We should probably always perform bounds checking, since going out of
     * bounds is likely to crash the JVM.
     */
    private static final boolean CHECK_BOUNDS = true;

    /* Number of bytes for storing the int block size. */
    private static final int SIZE_BYTES = 4;

    private final Unsafe unsafe;
    private final AtomicLong usedBytes = new AtomicLong(0);

    public CHeapAllocator() {

        /*
         * We cannot call Unsafe.getUnsafe because it throws
         * SecurityException when called from a non-bootstrap class. Getting
         * the static field (that would be returned by Unsafe.getUnsafe) is
         * better than calling the Unsafe private constructor, since Unsafe
         * is intended to have a singleton instance.
         */
        try {
            final Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Throwable e) {
            throw new UnsupportedOperationException(
                "Unable to get Unsafe object", e);
        }

        if (unsafe == null) {
            throw new UnsupportedOperationException(
                "Unsafe singleton is null");
        }

        /*
         * Check for seemingly obvious byte and int sizes, to ensure that the
         * JVM isn't doing something strange.
         */
        if (Unsafe.ARRAY_BYTE_INDEX_SCALE != 1) {
            throw new UnsupportedOperationException(
                "Unexpected Unsafe.ARRAY_BYTE_INDEX_SCALE: " +
                Unsafe.ARRAY_BYTE_INDEX_SCALE);
        }
        if (Unsafe.ARRAY_INT_INDEX_SCALE != SIZE_BYTES) {
            throw new UnsupportedOperationException(
                "Unexpected Unsafe.ARRAY_INT_INDEX_SCALE: " +
                    Unsafe.ARRAY_INT_INDEX_SCALE);
        }
    }

    @Override
    public void setMaxBytes(long maxBytes) {
    }

    @Override
    public long getUsedBytes() {

        return usedBytes.get();
    }

    @Override
    public long allocate(int size) {

        final int allocSize = size + SIZE_BYTES;
        final long memId = unsafe.allocateMemory(allocSize);

        unsafe.putInt(memId, size);
        unsafe.setMemory(memId + SIZE_BYTES, size, (byte) 0);
        usedBytes.addAndGet(addOverhead(allocSize));

        return memId;
    }

    @Override
    public int free(long memId) {

        final int totalSize = addOverhead(size(memId) + SIZE_BYTES);
        unsafe.freeMemory(memId);
        usedBytes.addAndGet(0 - totalSize);
        return totalSize;
    }

    private int addOverhead(int allocSize) {

        /* TODO: There is 70 bytes added overhead when using the IBM JDK. */

        /* Blocks are aligned on 8 byte boundaries with a 16 byte header. */
        allocSize += (allocSize % 8) + 16;

        /* The minimum block size is 24 bytes. */
        return (allocSize < 24) ? 24 : allocSize;
    }

    @Override
    public int size(long memId) {

        return unsafe.getInt(memId);
    }

    @Override
    public int totalSize(long memId) {

        return addOverhead(size(memId) + SIZE_BYTES);
    }

    @Override
    public void copy(long memId, int memOff, byte[] buf, int bufOff, int len) {

        if (CHECK_BOUNDS) {
            if (memId == 0) {
                throw new NullPointerException("memId is 0");
            }
            if (buf == null) {
                throw new NullPointerException("buf is null");
            }
            if (memOff < 0 || memOff + len > size(memId)) {
                throw new IndexOutOfBoundsException(
                    "memOff=" + memOff +
                    " memSize=" + size(memId) +
                    " copyLen=" + len);
            }
            if (bufOff < 0 || bufOff + len > buf.length) {
                throw new IndexOutOfBoundsException(
                    "bufOff=" + bufOff +
                    " bufSize=" + buf.length +
                    " copyLen=" + len);
            }
        }

        unsafe.copyMemory(
            null, memId + SIZE_BYTES + memOff,
            buf, Unsafe.ARRAY_BYTE_BASE_OFFSET + bufOff,
            len);
    }

    @Override
    public void copy(byte[] buf, int bufOff, long memId, int memOff, int len) {

        if (CHECK_BOUNDS) {
            if (memId == 0) {
                throw new NullPointerException("memId is 0");
            }
            if (buf == null) {
                throw new NullPointerException("buf is null");
            }
            if (memOff < 0 || memOff + len > size(memId)) {
                throw new IndexOutOfBoundsException(
                    "memOff=" + memOff +
                    " memSize=" + size(memId) +
                    " copyLen=" + len);
            }
            if (bufOff < 0 || bufOff + len > buf.length) {
                throw new IndexOutOfBoundsException(
                    "bufOff=" + bufOff +
                    " bufSize=" + buf.length +
                    " copyLen=" + len);
            }
        }

        unsafe.copyMemory(
            buf, Unsafe.ARRAY_BYTE_BASE_OFFSET + bufOff,
            null, memId + SIZE_BYTES + memOff,
            len);
    }

    @Override
    public void copy(long fromMemId,
                     int fromMemOff,
                     long toMemId,
                     int toMemOff,
                     int len) {

        if (CHECK_BOUNDS) {
            if (fromMemId == 0 || toMemId == 0) {
                throw new NullPointerException("memId is 0");
            }
            if (fromMemOff < 0 || fromMemOff + len > size(fromMemId)) {
                throw new IndexOutOfBoundsException(
                    "memOff=" + fromMemOff +
                    " memSize=" + size(fromMemId) +
                    " copyLen=" + len);
            }
            if (toMemOff < 0 || toMemOff + len > size(toMemId)) {
                throw new IndexOutOfBoundsException(
                    "memOff=" + toMemOff +
                    " memSize=" + size(toMemId) +
                    " copyLen=" + len);
            }
        }

        unsafe.copyMemory(
            null, fromMemId + SIZE_BYTES + fromMemOff,
            null, toMemId + SIZE_BYTES + toMemOff,
            len);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy