This is an automated email from the ASF dual-hosted git repository.

lhotari pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar.git


The following commit(s) were added to refs/heads/master by this push:
     new 0bc9dcbca3f [improve][broker] Update netty allocator default chunk 
size from 4MB to 8MB, and move internal JVM opts from PULSAR_EXTRA_OPTS to OPTS 
(#25274)
0bc9dcbca3f is described below

commit 0bc9dcbca3fb2d1a43a6cda630e2e34fdec6b8cd
Author: zhou zhuohan <[email protected]>
AuthorDate: Mon Mar 9 19:41:52 2026 +0800

    [improve][broker] Update netty allocator default chunk size from 4MB to 
8MB, and move internal JVM opts from PULSAR_EXTRA_OPTS to OPTS (#25274)
---
 bin/bookkeeper                                     | 20 ++++++++++++
 bin/function-localrunner                           | 29 ++++++++++++++---
 bin/pulsar                                         | 25 +++++++++++++++
 bin/pulsar-admin-common.cmd                        | 24 +++++++++++++++
 bin/pulsar-admin-common.sh                         | 24 +++++++++++++++
 bin/pulsar-perf                                    | 24 +++++++++++++++
 conf/bkenv.sh                                      |  3 +-
 conf/pulsar_env.sh                                 |  3 --
 conf/pulsar_tools_env.sh                           |  3 +-
 .../terraform-ansible/templates/pulsar_env.sh      |  3 --
 .../PulsarByteBufAllocatorDefaultTest.java         | 36 ++++++++++++++++++++++
 src/pulsar-io-gen.sh                               | 24 +++++++++++++++
 .../integration/containers/PulsarContainer.java    |  2 --
 13 files changed, 203 insertions(+), 17 deletions(-)

diff --git a/bin/bookkeeper b/bin/bookkeeper
index b61123e3c85..6a0fee9101a 100755
--- a/bin/bookkeeper
+++ b/bin/bookkeeper
@@ -209,6 +209,26 @@ OPTS="-Djava.net.preferIPv4Stack=true $OPTS"
 if [[ $JAVA_MAJOR_VERSION -ge 23 ]]; then
   OPTS="--sun-misc-unsafe-memory-access=allow $OPTS"
 fi
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.leakDetection.level=disabled 
-Dio.netty.recycler.maxCapacityPerThread=4096 -Dio.netty.allocator.maxOrder=10 
$OPTS"
 
 OPTS="$OPTS $BOOKIE_MEM $BOOKIE_GC $BOOKIE_GC_LOG $BOOKIE_EXTRA_OPTS"
 
diff --git a/bin/function-localrunner b/bin/function-localrunner
index 74def8fcd96..87f0a9a23b7 100755
--- a/bin/function-localrunner
+++ b/bin/function-localrunner
@@ -76,9 +76,6 @@ if [[ -z "$PULSAR_GC_LOG" ]]; then
   fi
 fi
 
-# Extra options to be passed to the jvm
-PULSAR_EXTRA_OPTS=${PULSAR_EXTRA_OPTS:-" -Dpulsar.allocator.exit_on_oom=true 
-Dio.netty.recycler.maxCapacityPerThread=4096"}
-
 if [ -z "$PULSAR_LOG_CONF" ]; then
     PULSAR_LOG_CONF=$DEFAULT_LOG_CONF
 fi
@@ -139,9 +136,31 @@ if [[ $JAVA_MAJOR_VERSION -ge 23 ]]; then
 fi
 
 OPTS="-cp $PULSAR_CLASSPATH $OPTS"
-
 # we should exit on OOM for localrun especially when using ThreadRuntime
-PULSAR_EXTRA_OPTS="$PULSAR_EXTRA_OPTS -XX:+ExitOnOutOfMemoryError"
+# These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+# if it runs out of either Java heap memory or its internal off-heap memory,
+# as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+OPTS="-XX:+ExitOnOutOfMemoryError -Dpulsar.allocator.exit_on_oom=true $OPTS"
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 $OPTS"
 
 OPTS="$OPTS $PULSAR_EXTRA_OPTS $PULSAR_MEM $PULSAR_GC"
 
diff --git a/bin/pulsar b/bin/pulsar
index b879019eb18..24897c3d3db 100755
--- a/bin/pulsar
+++ b/bin/pulsar
@@ -300,10 +300,35 @@ OPTS="$OPTS --add-opens java.base/java.nio=ALL-UNNAMED 
--add-opens java.base/jdk
 OPTS="$OPTS --add-opens java.base/jdk.internal.platform=ALL-UNNAMED"
 # Required by RocksDB java.lang.System::loadLibrary call
 OPTS="$OPTS --enable-native-access=ALL-UNNAMED"
+# These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+# if it runs out of either Java heap memory or its internal off-heap memory,
+# as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+OPTS="-XX:+ExitOnOutOfMemoryError -Dpulsar.allocator.exit_on_oom=true $OPTS"
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 $OPTS"
 
 OPTS="-cp $PULSAR_CLASSPATH $OPTS"
 
 if [ $COMMAND == "bookie" ]; then
+  OPTS="-Dio.netty.leakDetection.level=disabled $OPTS"
   # Pass BOOKIE_EXTRA_OPTS option defined in bkenv.sh
   OPTS="$OPTS $BOOKIE_MEM $BOOKIE_GC $BOOKIE_GC_LOG $BOOKIE_EXTRA_OPTS"
 else
diff --git a/bin/pulsar-admin-common.cmd b/bin/pulsar-admin-common.cmd
index e3f4754ea55..f2082f9bd8f 100644
--- a/bin/pulsar-admin-common.cmd
+++ b/bin/pulsar-admin-common.cmd
@@ -83,6 +83,30 @@ if %JAVA_MAJOR_VERSION% GEQ 11 (
   REM Required by Netty for optimized direct byte buffer access
   set "OPTS=%OPTS% --add-opens java.base/java.nio=ALL-UNNAMED --add-opens 
java.base/jdk.internal.misc=ALL-UNNAMED"
 )
+REM These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+REM if it runs out of either Java heap memory or its internal off-heap memory,
+REM as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+set "OPTS=-Dpulsar.allocator.exit_on_oom=true %OPTS%"
+REM Netty tuning
+REM These settings are primarily used to modify the Netty allocator 
configuration,
+REM improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+REM
+REM Based on the netty source code, the allocator's default chunk size is 
calculated as:
+REM io.netty.allocator.pageSize (default: 8192) shifted left by
+REM io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+REM This equals 8192 * 2^9 = 4 MB:
+REM 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+REM
+REM Allocations that are larger than chunk size are considered huge 
allocations and don't use the pool:
+REM 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+REM
+REM Currently, Pulsar defaults to a maximum single message size of 5 MB.
+REM Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+REM Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+REM This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+REM Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+REM than chunk size (8MB) and can reuse Netty's memory pool.
+set "OPTS=-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 %OPTS%"
 
 set "OPTS=-cp "%PULSAR_CLASSPATH%" %OPTS%"
 set "OPTS=%OPTS% %PULSAR_EXTRA_OPTS%"
diff --git a/bin/pulsar-admin-common.sh b/bin/pulsar-admin-common.sh
index 366d76d7f5b..6052b4726e0 100755
--- a/bin/pulsar-admin-common.sh
+++ b/bin/pulsar-admin-common.sh
@@ -126,6 +126,30 @@ if [[ $JAVA_MAJOR_VERSION -ge 11 ]]; then
   # Required by Netty for optimized direct byte buffer access
   OPTS="$OPTS --add-opens java.base/java.nio=ALL-UNNAMED --add-opens 
java.base/jdk.internal.misc=ALL-UNNAMED"
 fi
+# These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+# if it runs out of either Java heap memory or its internal off-heap memory,
+# as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+OPTS="-XX:+ExitOnOutOfMemoryError -Dpulsar.allocator.exit_on_oom=true $OPTS"
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 $OPTS"
 
 OPTS="-cp $PULSAR_CLASSPATH $OPTS"
 
diff --git a/bin/pulsar-perf b/bin/pulsar-perf
index 2b120cc2f94..1f9b25fd026 100755
--- a/bin/pulsar-perf
+++ b/bin/pulsar-perf
@@ -123,6 +123,30 @@ if [[ $JAVA_MAJOR_VERSION -ge 11 ]]; then
   # Required by Netty for optimized direct byte buffer access
   OPTS="$OPTS --add-opens java.base/java.nio=ALL-UNNAMED --add-opens 
java.base/jdk.internal.misc=ALL-UNNAMED"
 fi
+# These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+# if it runs out of either Java heap memory or its internal off-heap memory,
+# as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+OPTS="-XX:+ExitOnOutOfMemoryError -Dpulsar.allocator.exit_on_oom=true $OPTS"
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 $OPTS"
 
 OPTS="-cp $PULSAR_CLASSPATH $OPTS"
 OPTS="$OPTS $PULSAR_EXTRA_OPTS"
diff --git a/conf/bkenv.sh b/conf/bkenv.sh
index 5e8c2572a58..2e66bcacde6 100644
--- a/conf/bkenv.sh
+++ b/conf/bkenv.sh
@@ -88,8 +88,7 @@ if [[ -z "$BOOKIE_GC_LOG" ]]; then
   fi
 fi
 
-# Extra options to be passed to the jvm
-BOOKIE_EXTRA_OPTS="${BOOKIE_EXTRA_OPTS:-"-Dio.netty.leakDetection.level=disabled
 ${PULSAR_EXTRA_OPTS:-"-Dio.netty.recycler.maxCapacityPerThread=4096"}"}"
+BOOKIE_EXTRA_OPTS="${BOOKIE_EXTRA_OPTS} ${PULSAR_EXTRA_OPTS}"
 
 # Add extra paths to the bookkeeper classpath
 # BOOKIE_EXTRA_CLASSPATH=
diff --git a/conf/pulsar_env.sh b/conf/pulsar_env.sh
index 2189c887623..530a08c0ed7 100755
--- a/conf/pulsar_env.sh
+++ b/conf/pulsar_env.sh
@@ -89,9 +89,6 @@ if [[ -z "$PULSAR_GC_LOG" ]]; then
   fi
 fi
 
-# Extra options to be passed to the jvm
-PULSAR_EXTRA_OPTS="${PULSAR_EXTRA_OPTS:-" -Dpulsar.allocator.exit_on_oom=true 
-Dio.netty.recycler.maxCapacityPerThread=4096"}"
-
 # Add extra paths to the bookkeeper classpath
 # PULSAR_EXTRA_CLASSPATH=
 
diff --git a/conf/pulsar_tools_env.sh b/conf/pulsar_tools_env.sh
index 6020e0a863a..bc2b6e030d2 100755
--- a/conf/pulsar_tools_env.sh
+++ b/conf/pulsar_tools_env.sh
@@ -57,8 +57,7 @@ if [ -n "$PULSAR_MEM" ]; then
 fi
 PULSAR_MEM=${PULSAR_MEM:-"-Xmx128m -XX:MaxDirectMemorySize=128m"}
 
-# Extra options to be passed to the jvm
-PULSAR_EXTRA_OPTS="${PULSAR_MEM} ${PULSAR_GC} ${PULSAR_GC_LOG} 
-Dio.netty.leakDetection.level=disabled ${PULSAR_EXTRA_OPTS}"
+PULSAR_EXTRA_OPTS="${PULSAR_MEM} ${PULSAR_GC} ${PULSAR_GC_LOG} 
${PULSAR_EXTRA_OPTS}"
 
 # Add extra paths to the bookkeeper classpath
 # PULSAR_EXTRA_CLASSPATH=
diff --git a/deployment/terraform-ansible/templates/pulsar_env.sh 
b/deployment/terraform-ansible/templates/pulsar_env.sh
index 2638718ee55..6cda804f94a 100644
--- a/deployment/terraform-ansible/templates/pulsar_env.sh
+++ b/deployment/terraform-ansible/templates/pulsar_env.sh
@@ -47,9 +47,6 @@ PULSAR_MEM=" -Xms{{ max_heap_memory }} -Xmx{{ max_heap_memory 
}} -XX:MaxDirectMe
 # Garbage collection options
 PULSAR_GC=" -XX:+UseZGC -XX:+PerfDisableSharedMem -XX:+AlwaysPreTouch"
 
-# Extra options to be passed to the jvm
-PULSAR_EXTRA_OPTS="-Dio.netty.leakDetection.level=disabled 
-Dio.netty.recycler.maxCapacityPerThread=4096 ${PULSAR_EXTRA_OPTS}"
-
 # Add extra paths to the bookkeeper classpath
 # PULSAR_EXTRA_CLASSPATH=
 
diff --git 
a/pulsar-common/src/test/java/org/apache/pulsar/common/allocator/PulsarByteBufAllocatorDefaultTest.java
 
b/pulsar-common/src/test/java/org/apache/pulsar/common/allocator/PulsarByteBufAllocatorDefaultTest.java
index 961c6f0176d..f19f4a38ac2 100644
--- 
a/pulsar-common/src/test/java/org/apache/pulsar/common/allocator/PulsarByteBufAllocatorDefaultTest.java
+++ 
b/pulsar-common/src/test/java/org/apache/pulsar/common/allocator/PulsarByteBufAllocatorDefaultTest.java
@@ -23,6 +23,8 @@ import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
 import io.netty.buffer.ByteBufAllocator;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.buffer.PooledByteBufAllocatorMetric;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.bookkeeper.common.allocator.OutOfMemoryPolicy;
@@ -54,4 +56,38 @@ public class PulsarByteBufAllocatorDefaultTest {
         }
     }
 
+    /**
+     * Verify that a {@link PooledByteBufAllocator} created with {@code 
maxOrder=10} produces the expected chunk size,
+     * which is consistent with the {@code -Dio.netty.allocator.maxOrder=10} 
setting in {@code conf/pulsar_env.sh}.
+     *
+     * <p>Netty computes chunk size as: {@code pageSize << maxOrder = 8192 << 
10 = 8,388,608 bytes (8 MiB)}.
+     * This test constructs the allocator directly with {@code maxOrder=10} so 
no JVM argument is required.
+     */
+    @Test
+    public void testDefaultChunkSizeMatchesMaxOrder10() {
+        // Expected chunk size: pageSize (8192 bytes) << maxOrder (10) = 8 MiB
+        final int maxOrder = 10;
+        final int expectedChunkSize = 8192 << maxOrder;
+
+        // Create a PooledByteBufAllocator with maxOrder=10, same as 
-Dio.netty.allocator.maxOrder=10
+        PooledByteBufAllocator allocator = new PooledByteBufAllocator(
+                true,           // preferDirect
+                0,                        // nHeapArena
+                1,                        // nDirectArena
+                8192,                     // pageSize (default)
+                maxOrder,                 // maxOrder=10
+                64,                       // smallPageSize (default)
+                256,                      // normalPageSize (default)
+                false,
+                0
+        );
+
+        PooledByteBufAllocatorMetric metric = allocator.metric();
+        // Verify that the chunk size derived from maxOrder=10 equals 8 MiB
+        assertEquals(metric.chunkSize(), expectedChunkSize,
+                "Chunk size should be 8 MiB (pageSize << maxOrder = 8192 << 
10) "
+                        + "as configured by -Dio.netty.allocator.maxOrder=10 
in pulsar_env.sh");
+
+    }
+
 }
\ No newline at end of file
diff --git a/src/pulsar-io-gen.sh b/src/pulsar-io-gen.sh
index 2748d8b13c4..f72de767849 100755
--- a/src/pulsar-io-gen.sh
+++ b/src/pulsar-io-gen.sh
@@ -114,6 +114,30 @@ OPTS="-Djava.net.preferIPv4Stack=true $OPTS 
-Dlog4j.configurationFile=`basename
 if [[ $JAVA_MAJOR_VERSION -ge 23 ]]; then
   OPTS="--sun-misc-unsafe-memory-access=allow $OPTS"
 fi
+# These two settings work together to ensure the Pulsar process exits 
immediately and predictably
+# if it runs out of either Java heap memory or its internal off-heap memory,
+# as these are unrecoverable errors that require a process restart to clear 
the faulty state and restore operation
+OPTS="-XX:+ExitOnOutOfMemoryError -Dpulsar.allocator.exit_on_oom=true $OPTS"
+# Netty tuning
+# These settings are primarily used to modify the Netty allocator 
configuration,
+# improving memory utilization and reducing the frequency of requesting 
off-heap memory from the OS
+#
+# Based on the netty source code, the allocator's default chunk size is 
calculated as:
+# io.netty.allocator.pageSize (default: 8192) shifted left by
+# io.netty.allocator.maxOrder (default: 9 after Netty 4.1.76.Final version).
+# This equals 8192 * 2^9 = 4 MB:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java#L105
+#
+# Allocations that are larger than chunk size are considered huge allocations 
and don't use the pool:
+# 
https://github.com/netty/netty/blob/4.1/buffer/src/main/java/io/netty/buffer/PoolArena.java#L141-L142
+#
+# Currently, Pulsar defaults to a maximum single message size of 5 MB.
+# Therefore, when frequently producing messages whose size exceeds the chunk 
size,
+# Netty cannot utilize resources from the memory pool and must frequently 
allocate native memory.
+# This can lead to increased physical memory fragmentation and higher 
reclamation costs.
+# Thus, increasing io.netty.allocator.maxOrder to 10 to ensure that a single 
message is larger
+# than chunk size (8MB) and can reuse Netty's memory pool.
+OPTS="-Dio.netty.recycler.maxCapacityPerThread=4096 
-Dio.netty.allocator.maxOrder=10 $OPTS"
 
 OPTS="-cp $PULSAR_CLASSPATH $OPTS"
 OPTS="$OPTS $PULSAR_EXTRA_OPTS"
diff --git 
a/tests/integration/src/test/java/org/apache/pulsar/tests/integration/containers/PulsarContainer.java
 
b/tests/integration/src/test/java/org/apache/pulsar/tests/integration/containers/PulsarContainer.java
index 2dd750b60e3..e051211f645 100644
--- 
a/tests/integration/src/test/java/org/apache/pulsar/tests/integration/containers/PulsarContainer.java
+++ 
b/tests/integration/src/test/java/org/apache/pulsar/tests/integration/containers/PulsarContainer.java
@@ -300,8 +300,6 @@ public abstract class PulsarContainer<SelfT extends 
PulsarContainer<SelfT>> exte
     }
 
     protected void initializePulsarExtraOpts() {
-        appendToEnv("PULSAR_EXTRA_OPTS",
-                "-Dpulsar.allocator.exit_on_oom=true 
-Dio.netty.recycler.maxCapacityPerThread=4096");
     }
 
     protected boolean isCodeCoverageEnabled() {

Reply via email to