Class S3DirectBufferPoolOptions
- java.lang.Object
-
- software.amazon.awssdk.crt.s3.S3DirectBufferPoolOptions
-
public final class S3DirectBufferPoolOptions extends Object
Settings for an optional direct buffer pool that holds anS3Client's transfer memory in Java direct memory. To turn the pool on, pass these options toS3ClientOptions.withDirectBufferPoolOptions(S3DirectBufferPoolOptions)when you create the client.The client splits each upload and download into parts and holds the parts in flight in memory. By default that memory is native memory the JVM cannot see or limit. With the pool, the memory comes from
direct ByteBuffersinstead: it counts against-XX:MaxDirectMemorySize, never grows past a limit you control, and (except withfixed(long)) is given back when the client is idle.Opt-in
The pool is off unless you set these options. Without them the client behaves exactly as before. With them, existing response handlers keep working unchanged; reading response data without a copy is a separate, optional step (see Delivery).Example
S3ClientOptions options = new S3ClientOptions() .withRegion("us-west-2") .withClientBootstrap(bootstrap) .withCredentialsProvider(credentialsProvider) .withDirectBufferPoolOptions(S3DirectBufferPoolOptions.auto()); S3Client client = new S3Client(options);Choosing a factory
auto(): recommended. Sized automatically; grows with demand and shrinks when idle.fixed(long): a fixed amount of memory, allocated up front. Use it when you need a hard, predictable memory budget.elastic(long, long): grows and shrinks between a minimum and maximum you choose.
Sizing
Every pool has a floor and a ceiling:- Floor: memory allocated when the client is created and kept even while the client is idle.
- Ceiling: the most memory the pool will ever use. When it is reached, new requests wait until memory is freed.
S3ClientOptions.withPartSize(long)), so byte sizes you pass are rounded to whole parts. Between the floor and the ceiling, the pool grows as needed and gives memory back when idle.Choosing the ceiling. What the ceiling needs to cover depends on how many requests run at once, not on how large the objects are:
- With backpressure enabled
(
S3ClientOptions.withReadBackpressureEnabled(boolean)), each request holds up to its read window plus one part:ceiling >= concurrent requests x (read window + part size)
The extra part is because a part starts downloading as soon as the window covers any of its bytes, so a request can hold one part beyond its window.S3ClientOptions.withInitialReadWindowSize(long)is only a client-wide starting value:S3MetaRequest.incrementReadWindow(long)raises one request's window with no upper bound, so if you use it, size the ceiling for the sum of the windows you grant rather than the starting value. - For objects smaller than one part:
ceiling >= concurrent requests x part size
Memory is reserved a whole part at a time, so an object smaller than a part still holds a full part while it is in flight. Sizing from the bytes actually in flight under-sizes the pool: with an 8 MiB part size, 100 concurrent 256 KiB downloads need 800 MiB, not 25 MiB. ReduceS3ClientOptions.withPartSize(long)if that is too much for a small-object workload.
How
S3ClientOptions.withMemoryLimitInBytes(long)works with the pool depends on the factory:auto(): it sets the ceiling. Use it to cap how much memory the pool can use.fixed(long)andelastic(long, long): the size you pass to the factory is the ceiling, so you don't need to setS3ClientOptions.withMemoryLimitInBytes(long). If it is set, it is an upper bound: the pool's ceiling (the factory size rounded down to whole parts) must not exceed it, or creating the client fails and the error message gives the values to use.
JVM direct memory limit: the pool's memory counts against
-XX:MaxDirectMemorySize, which by default equals the maximum heap size (-Xmx). The ceiling must fit within 80% of that limit. If you chose the ceiling (withfixed(long),elastic(long, long),withMemoryLimitInBytesor an environment variable) and it doesn't fit, creating the client throws; raise-XX:MaxDirectMemorySizeor choose a smaller ceiling. A default ceiling picked byauto()is reduced to fit instead. An explicit-XX:MaxDirectMemorySize=0allows no direct memory, so creating the client always throws.Delivery
A handler that implementsS3BorrowedBufferDeliveryreceives each chunk straight from pool memory, with no copy into a heapbyte[]; any other handler keeps receivingS3MetaRequestResponseHandler.onResponseBody(ByteBuffer, long, long). Exactly one of the two is called per request. SeeS3BorrowedBufferfor the details, including the part-number GetObject exception.Uploads
Multipart uploads hold each part in this pool until the part completes, including retries; single-part uploads don't use it. Uploads and downloads share the same ceiling, so a large upload can make downloads wait, and the other way round. Uploads get the same memory visibility and limit, but no copy is avoided:S3BorrowedBufferis for downloads only. Limits on upload part size are listed under Large parts.Lifetime
The client creates the pool and owns it; each client has its own. When the client shuts down, the pool frees its unused memory immediately. Borrowed buffers keep their memory until they are closed, even after shutdown; seeS3BorrowedBuffer.Large parts
Usually there is nothing to do: the client uses larger parts on its own when it needs them, for example for uploads too big for S3's 10,000-part limit at the configured part size. These limits apply:- If you set
partSizeexplicitly, it is never raised: an upload that would need larger parts is rejected when the request is made, with the part size to use. - A resumed upload uses the part size recorded in its resume token rather than the client's, and is rejected the same way if the pool cannot hold it.
- An upload part is bounded by the ceiling and by S3's own maximum; an upload needing larger parts is rejected when the request is made.
partSizehas an upper bound, or creating the client fails.
fixed(long)pools have a further limit on very large uploads; see that method. A request that can never fit in the pool fails, with the reason logged.Instances are immutable and may be reused across clients; each client builds its own pool from them.
-
-
Method Summary
All Methods Static Methods Concrete Methods Modifier and Type Method Description static S3DirectBufferPoolOptionsauto()Recommended default.static S3DirectBufferPoolOptionselastic(long minBytes, long maxBytes)Pool with a floor and ceiling you choose, in bytes.static S3DirectBufferPoolOptionsfixed(long memoryLimitBytes)Fixed-size pool: all of its memory is allocated when the client is created, and it never grows or shrinks.
-
-
-
Method Detail
-
auto
public static S3DirectBufferPoolOptions auto()
Recommended default. Sizes the pool automatically, so no tuning is needed.The ceiling is the most memory the pool will use. It is taken from the sources below, in priority order: the first one that is set is used, and the rest are ignored.
S3ClientOptions.withMemoryLimitInBytes(long)- the
AWS_CRT_S3_MEMORY_LIMIT_IN_MBenvironment variable (in MiB) - the
AWS_CRT_S3_MEMORY_LIMIT_IN_GIBenvironment variable - a default based on
S3ClientOptions.withThroughputTargetGbps(double)(if unset, detected from the EC2 instance type, so the ceiling can differ between instances). If this default doesn't fit within 80% of the JVM direct memory limit, it is reduced to fit and a warning is logged, since a smaller pool can limit throughput; raise-XX:MaxDirectMemorySizeto use the full default.
The floor is 16 parts (128 MiB with an 8 MiB part size), or the whole pool if the ceiling is smaller. Above that, the pool grows on demand and shrinks when idle. Each time it grows it briefly delays other requests on the client; see
elastic(long, long)for details.- Returns:
- options for an automatically sized pool
-
fixed
public static S3DirectBufferPoolOptions fixed(long memoryLimitBytes)
Fixed-size pool: all of its memory is allocated when the client is created, and it never grows or shrinks. Use it when you need a hard, predictable memory budget, for example in a tight container.- Size:
memoryLimitBytes, rounded down to whole parts (S3ClientOptions.withPartSize(long)). - When full: new requests wait until memory frees up.
- Very large uploads: each upload part must fit in 4 parts' worth of memory. Affected are uploads big enough that S3's 10,000-part limit forces a larger part size (over about 312 GiB with an 8 MiB part size), and resumed uploads whose token specifies a larger part size; they are rejected when the request is made, with the reason. Downloads are not affected.
- No allocation pauses: memory is never allocated or freed after the client is created. The cost moves to client creation instead, which allocates and zero-fills the whole pool: expect a few hundred milliseconds per GiB.
- Parameters:
memoryLimitBytes- total direct-memory budget for the pool, counted against-XX:MaxDirectMemorySize; must be at least one part, checked when the client is created- Returns:
- options for a fixed pool
- Throws:
IllegalArgumentException- ifmemoryLimitBytes <= 0
- Size:
-
elastic
public static S3DirectBufferPoolOptions elastic(long minBytes, long maxBytes)
Pool with a floor and ceiling you choose, in bytes. Use it to size the pool yourself while still letting it grow and shrink with demand.Floor and ceiling mean what the class doc's Sizing section says; this factory only decides their values. The pool works in 16-part groups, so the floor is
minBytesrounded down to a multiple of 16 parts (or the whole pool ifminBytesreaches the ceiling) and never more than you asked for, the ceiling ismaxBytes, and in between the pool grows 16 parts at a time and releases everything above the floor once the client goes idle.maxBytesneed not be a multiple of 16 parts: it rounds down to whole parts, and the last group holds whatever is left.Note: growth cost. Each time the pool grows it allocates and zero-fills 16 parts of memory (128 MiB with 8 MiB parts), which takes several milliseconds and briefly delays other requests on the client. A sudden burst can grow the pool several times in quick succession, and the same happens again after the pool has shrunk while idle. If your workload can't tolerate these pauses, use
fixed(long).- Parameters:
minBytes- memory allocated when the client is created and kept while idle (>= 0; 0 allocates nothing up front)maxBytes- most memory the pool will use (>= minBytes; must be at least one part, checked when the client is created)- Returns:
- options for an elastic pool
- Throws:
IllegalArgumentException- ifminBytes < 0,maxBytes <= 0, orminBytes > maxBytes
-
-