public interface OffHeapAllocator
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.
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
getUsedBytes().
Note that with the default allocator, the size is not a built-in property
of each block, and the size(long) method will be implemented by storing
the size at the front of the block. The size method is included in
the interface to allow for implementations where a block size property is
naturally available.
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.
All methods in the allocator must be thread safe, and contention among threads should be minimized.
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.
| Modifier and Type | Interface and Description |
|---|---|
static class |
OffHeapAllocator.OffHeapOverflowException |
| Modifier and Type | Method and Description |
|---|---|
long |
allocate(int size)
Allocates a block of a given size and returns its ID.
|
void |
copy(byte[] buf,
int bufOff,
long memId,
int memOff,
int len)
Copies bytes from a Java byte array to an allocated block.
|
void |
copy(long memId,
int memOff,
byte[] buf,
int bufOff,
int len)
Copies bytes from an allocated block to a Java byte array.
|
void |
copy(long fromMemId,
int fromMemOff,
long toMemId,
int toMemOff,
int len)
Copies bytes from one allocated block to another.
|
int |
free(long memId)
Frees a block previously allocated and returns the size freed, including
any overhead for the block that is now free.
|
long |
getUsedBytes()
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.
|
void |
setMaxBytes(long maxBytes)
Sets the maximum size of the off-heap cache, to be used as a hint for
the creation of implementation specific data structures.
|
int |
size(long memId)
Returns the size of an allocated block.
|
int |
totalSize(long memId)
Returns the size of an allocated block plus any overhead for the block.
|
void setMaxBytes(long maxBytes)
allocate(int) method. In other words, JE will not assume that it can
allocate blocks totaling the specified maximum size. See getUsedBytes().
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.
allocate(int)long getUsedBytes()
allocate(int)long allocate(int size)
throws OutOfMemoryError,
OffHeapAllocator.OffHeapOverflowException
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
allocate(int) method may fail (thrown an exception) even when the
used size is less than the maximum bytes (the value passed to setMaxBytes(long)). JE handles this situation as follows.
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 setMaxBytes(long). JE calls
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 allocate(int) method when the used size exceeds
the limit.
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 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
OffHeapAllocator.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.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.getUsedBytes()int free(long memId)
int size(long memId)
int totalSize(long memId)
void copy(long memId,
int memOff,
byte[] buf,
int bufOff,
int len)
void copy(byte[] buf,
int bufOff,
long memId,
int memOff,
int len)
void copy(long fromMemId,
int fromMemOff,
long toMemId,
int toMemOff,
int len)
Copyright © 2024. All rights reserved.