blob: dfadbbf41fc5769b0a5e8223b4b111bf2e905657 [file] [log] [blame]
/*-
* 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;
/**
* Implemented by off-heap memory allocators.
*
* The allocator is responsible for allocating and freeing a block of memory
* efficiently, maintaining the size of a block, identifying a block by a long
* integer value (ID), and looking up a memory ID efficiently (in constant
* time) in order to copy bytes, return the size, or free the block.
* <p>
* The allocator is also responsible for compacting memory when necessary to
* perform allocations efficiently. A special case is when the off-heap cache
* is reduced in size, and memory should be compacted to make memory available
* to the OS; the implementation should account for this case.
* <p>
* Another responsibility of the allocator is to estimate the RAM usage for all
* blocks currently allocated, including overhead and space taken by
* fragmentation. It is recognized that this may only be a rough estimate in
* some implementations (the default allocator, for example). See
* {@link #getUsedBytes}.
* <p>
* Note that with the default allocator, the size is not a built-in property
* of each block, and the {@link #size} method will be implemented by storing
* the size at the front of the block. The {@code size} method is included in
* the interface to allow for implementations where a block size property is
* naturally available.
* <p>
* This interface requires that memory is copied in and out of the Java address
* space to make use of the off-heap cache. A future enhancement might involve
* adding a way to obtain a ByteBuffer for direct access to the off-heap memory
* block, to avoid the copy if this is possible in some implementations. In the
* default implementation, this is not practical without using non-public JVM
* internals and risking incompatibilities.
* <p>
* All methods in the allocator must be thread safe, and contention among
* threads should be minimized.
* <p>
* The memory blocks are not assumed to be fast access RAM and in particular
* might be NVRAM. JE makes an effort to only copy memory to/from the block
* when necessary, and to use single larger copy operations rather than
* multiple smaller copy operations.
*/
public interface OffHeapAllocator {
class OffHeapOverflowException extends Exception {}
/**
* Sets the maximum size of the off-heap cache, to be used as a hint for
* the creation of implementation specific data structures.
*
* The maximum cache size is the amount of RAM that the app would like to
* use for the off-heap cache, at the gross level of dividing up the RAM on
* a machine among processes, the off-heap cache, the file system cache,
* etc. Because there is overhead with any allocation scheme, less bytes
* will actually be available for memory blocks created with the {@link
* #allocate} method. In other words, JE will not assume that it can
* allocate blocks totaling the specified maximum size. See {@link
* #getUsedBytes }.
* <p>
* This method is always called once before any other method is called. It
* may be called more than once if the off-heap cache is resized by the
* app.
*
* @see #allocate(int)
*/
void setMaxBytes(long maxBytes);
/**
* Returns an estimate of the amount of RAM used by the cache, including
* the metadata and block overhead used by the implementation, as well as
* any free space needed for performing compaction.
*
* This method should not cause thread contention when called frequently
* from multiple threads. A volatile long field is the suggested
* implementation.
*
* @see #allocate(int)
*/
long getUsedBytes();
/**
* Allocates a block of a given size and returns its ID.
*
* The bytes of the memory block must be initialized to zero.
* <p>
* Note that because the used cache size is only an estimate, and in fact
* the maximum size might not actually be available (due to memory use by
* other processes when using the default allocator, for example), then the
* {@link #allocate} method may fail (thrown an exception) even when the
* used size is less than the maximum bytes (the value passed to {@link
* #setMaxBytes}). JE handles this situation as follows.
* <p>
* JE uses an internal off-heap cache size limit to determine when to
* perform eviction (which frees off-heap blocks). The limit is
* initialized to the value passed to {@link #setMaxBytes}. JE calls
* {@link #getUsedBytes()} to determine when to evict memory from the
* cache. If the used bytes by grows very close to the limit or exceeds
* it, JE will perform off-heap cache eviction. JE will make a best effort
* not to call the {@link #allocate} method when the used size exceeds
* the limit.
* <p>
* If an allocation failure occurs (i.e., this method throws an
* exception), JE adjusts the limit downward to account for the
* inaccuracies discussed above. When a RuntimeException is thrown, the
* limit is set to the used size; when an OutOfMemoryError is thrown, the
* limit is set to the used size minus the {@link
* com.sleepycat.je.EnvironmentConfig#OFFHEAP_EVICT_BYTES}. This adjustment
* should ensure that JE eviction occurs and prevent frequent allocation
* failures and associated exception handling.
* TODO: This never happens because Linux kills the process
* <p>
*
* @return non-zero memory ID, or zero when the memory cannot be allocated.
*
* @throws OffHeapOverflowException if the block cannot be allocated
* because the max size has been reached. The internal off-heap cache
* size limit will be set as described above.
*
* @throws OutOfMemoryError if the block cannot be allocated because no
* system memory is available. The internal off-heap cache size limit will
* be set as described above. In addition, a SEVERE message for the
* exception will be logged.
*
* @see #getUsedBytes
*/
long allocate(int size)
throws OutOfMemoryError, OffHeapOverflowException;
/**
* Frees a block previously allocated and returns the size freed, including
* any overhead for the block that is now free.
*/
int free(long memId);
/**
* Returns the size of an allocated block.
*/
int size(long memId);
/**
* Returns the size of an allocated block plus any overhead for the block.
*/
int totalSize(long memId);
/**
* Copies bytes from an allocated block to a Java byte array.
*/
void copy(long memId, int memOff, byte[] buf, int bufOff, int len);
/**
* Copies bytes from a Java byte array to an allocated block.
*/
void copy(byte[] buf, int bufOff, long memId, int memOff, int len);
/**
* Copies bytes from one allocated block to another.
*/
void copy(long fromMemId,
int fromMemOff,
long toMemId,
int toMemOff,
int len);
}