Mathias Roeppischer created IGNITE-28980:
--------------------------------------------
Summary: BinaryMemoryAllocator$ThreadLocalAllocator$Chunk does not
reset maxMsgSize after shrink check
Key: IGNITE-28980
URL: https://issues.apache.org/jira/browse/IGNITE-28980
Project: Ignite
Issue Type: Bug
Components: general
Affects Versions: 2.18
Environment: * Apache Ignite 2.18.0
* {{BinaryMemoryAllocator$ThreadLocalAllocator$Chunk}}
* Java 25.0.3+9
* Linux 6.8.0-100-generic amd64
Reporter: Mathias Roeppischer
Assignee: Mathias Roeppischer
Fix For: 2.19
{{BinaryMemoryAllocator$ThreadLocalAllocator$Chunk}} keeps track of the maximum
message size detected between checks in {{maxMsgSize}}.
The value is updated as follows:
{code:java}
if (maxMsgSize > this.maxMsgSize)
this.maxMsgSize = maxMsgSize;
{code}
The shrink logic periodically checks this value based on {{CHECK_FREQ}}:
{code:java}
if (CommonUtils.nanosToMillis(nowNanos - lastCheckNanos) >= CHECK_FREQ) {
int halfSize = data.length >> 1;
if (this.maxMsgSize < halfSize)
this.data = new byte[halfSize];
lastCheckNanos = nowNanos;
}
{code}
However, {{this.maxMsgSize}} is never reset after the check.
As a result, the value effectively represents the maximum message size seen
during the entire lifetime of the {{Chunk}}, rather than the maximum message
size detected between checks as documented.
h3. Impact
A single large serialization can permanently prevent the chunk from shrinking
again.
For example, if a chunk grows to 128 MB due to a large message and
{{maxMsgSize}} reaches 100 MB, subsequent small messages cannot lower
{{maxMsgSize}} below 100 MB. Consequently, the shrink condition
{code:java}
this.maxMsgSize < halfSize
{code}
may never become true, even if the chunk is subsequently used only for much
smaller messages.
This can cause large byte arrays to remain retained by thread-local allocators.
In applications using thread pools, a number of worker threads can therefore
retain large buffers after processing occasional large messages, resulting in
unnecessarily high heap usage.
h3. Expected behavior
{{maxMsgSize}} should represent the maximum message size observed during the
current check interval.
After the shrink check has been performed, the recorded maximum should be reset
so that the next interval starts with a fresh measurement.
h3. Proposed fix
Reset {{maxMsgSize}} after each {{CHECK_FREQ}} check:
{code:java}
if (CommonUtils.nanosToMillis(nowNanos - lastCheckNanos) >= CHECK_FREQ) {
int halfSize = data.length >> 1;
if (this.maxMsgSize < halfSize)
this.data = new byte[halfSize];
this.maxMsgSize = 0;
lastCheckNanos = nowNanos;
}
{code}
This preserves the existing design of collecting the maximum message size
between checks while allowing the chunk to shrink when subsequent serialization
workloads are smaller.
h3. Additional Information
Discussion on the Ignite User Mailing list:
https://lists.apache.org/thread/cr2rvdpyh1njn2wghvg1mq7gr66wgjls
--
This message was sent by Atlassian Jira
(v8.20.10#820010)