2020-08-28 14:02:51 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2020 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
2020-07-24 19:38:48 +02:00
|
|
|
package io.netty.buffer.b2;
|
|
|
|
|
|
|
|
import jdk.incubator.foreign.MemorySegment;
|
|
|
|
|
2020-07-29 10:30:03 +02:00
|
|
|
import static io.netty.buffer.b2.BBuf.*;
|
2020-07-24 19:38:48 +02:00
|
|
|
|
2020-08-24 16:03:02 +02:00
|
|
|
/**
|
2020-08-24 17:13:04 +02:00
|
|
|
* Interface for {@link Buf} allocators.
|
2020-08-24 16:03:02 +02:00
|
|
|
*/
|
2020-07-24 19:38:48 +02:00
|
|
|
public interface Allocator extends AutoCloseable {
|
2020-08-17 16:09:38 +02:00
|
|
|
static void checkSize(long size) {
|
|
|
|
if (size < 1) {
|
|
|
|
throw new IllegalArgumentException("Buffer size must be positive, but was " + size + '.');
|
|
|
|
}
|
|
|
|
// We use max array size because on-heap buffers will be backed by byte-arrays.
|
|
|
|
int maxArraySize = Integer.MAX_VALUE - 8;
|
|
|
|
if (size > maxArraySize) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"Buffer size cannot be greater than " + maxArraySize + ", but was " + size + '.');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-24 16:03:02 +02:00
|
|
|
/**
|
2020-08-24 17:13:04 +02:00
|
|
|
* Allocate a {@link Buf} of the given size in bytes. This method may throw an {@link OutOfMemoryError} if there is
|
|
|
|
* not enough free memory available to allocate a {@link Buf} of the requested size.
|
2020-08-24 16:03:02 +02:00
|
|
|
*
|
2020-08-24 17:13:04 +02:00
|
|
|
* @param size The size of {@link Buf} to allocate.
|
|
|
|
* @return The newly allocated {@link Buf}.
|
2020-08-24 16:03:02 +02:00
|
|
|
*/
|
2020-08-28 12:17:41 +02:00
|
|
|
Buf allocate(long size);
|
2020-07-24 19:38:48 +02:00
|
|
|
|
2020-08-24 16:03:02 +02:00
|
|
|
/**
|
|
|
|
* Close this allocator, freeing all of its internal resources. It is not specified if the allocator can still be
|
|
|
|
* used after this method has been called on it.
|
|
|
|
*/
|
2020-07-24 19:38:48 +02:00
|
|
|
@Override
|
|
|
|
default void close() {
|
|
|
|
}
|
|
|
|
|
|
|
|
static Allocator heap() {
|
|
|
|
return new Allocator() {
|
|
|
|
@Override
|
2020-07-29 10:30:03 +02:00
|
|
|
public BBuf allocate(long size) {
|
2020-08-17 16:09:38 +02:00
|
|
|
checkSize(size);
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
var segment = allocateHeap(size);
|
2020-07-29 10:30:03 +02:00
|
|
|
return new BBuf(segment, SEGMENT_CLOSE);
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static Allocator direct() {
|
|
|
|
return new Allocator() {
|
|
|
|
@Override
|
2020-08-24 17:13:04 +02:00
|
|
|
public Buf allocate(long size) {
|
2020-08-17 16:09:38 +02:00
|
|
|
checkSize(size);
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
var segment = allocateNative(size);
|
|
|
|
return new BBuf(segment, SEGMENT_CLOSE);
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-09-25 16:56:03 +02:00
|
|
|
static Allocator directWithCleaner() {
|
|
|
|
return new Allocator() {
|
|
|
|
@Override
|
|
|
|
public Buf allocate(long size) {
|
|
|
|
checkSize(size);
|
|
|
|
var segment = allocateNative(size);
|
|
|
|
segment.registerCleaner(Statics.CLEANER);
|
|
|
|
return new BBuf(segment, SEGMENT_CLOSE);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-07-24 19:38:48 +02:00
|
|
|
static Allocator pooledHeap() {
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return new SizeClassedMemoryPool() {
|
2020-07-24 19:38:48 +02:00
|
|
|
@Override
|
|
|
|
protected MemorySegment createMemorySegment(long size) {
|
2020-08-17 16:09:38 +02:00
|
|
|
checkSize(size);
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return allocateHeap(size).withOwnerThread(null);
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static Allocator pooledDirect() {
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return new SizeClassedMemoryPool() {
|
2020-07-24 19:38:48 +02:00
|
|
|
@Override
|
|
|
|
protected MemorySegment createMemorySegment(long size) {
|
2020-08-17 16:09:38 +02:00
|
|
|
checkSize(size);
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return allocateNative(size).withOwnerThread(null);
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2020-08-10 12:15:02 +02:00
|
|
|
|
|
|
|
static Allocator pooledDirectWithCleaner() {
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return new SizeClassedMemoryPool() {
|
2020-08-10 12:15:02 +02:00
|
|
|
@Override
|
|
|
|
protected MemorySegment createMemorySegment(long size) {
|
2020-08-17 16:09:38 +02:00
|
|
|
checkSize(size);
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
return allocateNative(size).withOwnerThread(null);
|
2020-08-10 12:15:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected BBuf createBBuf(MemorySegment segment) {
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
var drop = new NativeMemoryCleanerDrop(this, getDrop());
|
2020-08-10 12:15:02 +02:00
|
|
|
var buf = new BBuf(segment, drop);
|
|
|
|
drop.accept(buf);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
Make cleaner in pooledDirectWithCleaner return segments to pool instead of deallocating
Motivation:
Allocating memory is expensive, which is why we pool our allocations.
The cleaner, when used with pooled memory, should then return that memory to the pool instead of deallocating it.
The purpose of the cleaner is, after all, to avoid having to track the reference counts so precisely.
Modification:
The NativeMemoryCleanerDrop is now able to either recover lost memory segments, when a buffer wasn't closed explicitly
and is being cleaned by the GC, or return the buffer to the pool via ordinary drop.
The GatedCleanable has been added, because buffer ownership transfer involves generating new memory segments,
and in those cases we need to invalidate the old cleanables without deallocating the tracked memory segment or returning
it to the pool more than once.
Result:
The pooledDirectWithCleaner allocator is now able to reuse memory segments, even when their references are forgotten and
they processed by the cleaner thread.
2020-09-25 12:15:45 +02:00
|
|
|
|
|
|
|
private static MemorySegment allocateHeap(long size) {
|
|
|
|
return MemorySegment.ofArray(new byte[Math.toIntExact(size)]);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static MemorySegment allocateNative(long size) {
|
|
|
|
var segment = MemorySegment.allocateNative(size)
|
|
|
|
.withCleanupAction(Statics.getCleanupAction(size));
|
|
|
|
Statics.MEM_USAGE_NATIVE.add(size);
|
|
|
|
return segment;
|
|
|
|
}
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|