Before Creating the Enhancement Request
Summary
Three independent per-RPC/per-metrics micro-allocations can be eliminated to reduce unnecessary heap pressure:
- Logback
BasicStatusManager — internal status events accumulate during XML configuration loading without being consumed.
TopicMessageType.getMetricsValue() — allocates a new String on every call via value.toLowerCase().
RemotingHelper.getRequestCodeDesc() / getResponseCodeDesc() — eagerly evaluates String.valueOf(code) in Map.getOrDefault() even on cache hit.
Motivation
JFR settings=profile on a broker under steady-state send load reveals these three allocation sites as recurring per-RPC waste:
- Logback status events (
InfoStatus, BodyEvent) accumulate in BasicStatusManager retained set (~655 KiB) despite never being queried in production. Adding NopStatusListener suppresses the callback overhead.
TopicMessageType.getMetricsValue() is called once per send for metrics labeling. The toLowerCase() result is deterministic per enum constant — caching it in the constructor avoids ~1 String + 1 byte[] per RPC.
RemotingHelper.getRequestCodeDesc() and getResponseCodeDesc() are called per RPC response. Java eagerly evaluates String.valueOf(code) as a method argument to Map.getOrDefault(), allocating a String even when the key exists in the map. A simple get() + null check eliminates this on the hot (cache-hit) path.
Combined, these save ~200–300 bytes per RPC with zero behavioral change.
Describe the Solution Youd Like
-
Logback: Add <statusListener class="ch.qos.logback.core.status.NopStatusListener"/> after <configuration> in rmq.broker.logback.xml.
-
TopicMessageType: Cache metricsValue as a final String field initialized in the enum constructor with Locale.ROOT. The getter returns the cached reference directly.
-
RemotingHelper: Replace REQUEST_CODE_MAP.getOrDefault(code, String.valueOf(code)) with:
String desc = REQUEST_CODE_MAP.get(code);
return desc != null ? desc : String.valueOf(code);
Same pattern for RESPONSE_CODE_MAP.
Describe Alternatives Youve Considered
- Removing NopStatusListener: Considered using a conditional listener or
loggerContext.getStatusManager().clear() in code. Rejected because the XML-level listener is simpler and doesnt require broker startup changes.
String.intern() for metrics values: Rejected due to global String table contention and lack of locale control.
computeIfAbsent for RemotingHelper: Overkill — the map is a static Map<Integer, String> populated at class load time. A null check is simpler and avoids lambda allocation.
Additional Context
PR: #10491
This PR is stacked on PR #10443 (AttributeKey statics) and will be rebased on develop after #10443 merges to show only the 3 files above.
Before Creating the Enhancement Request
Summary
Three independent per-RPC/per-metrics micro-allocations can be eliminated to reduce unnecessary heap pressure:
BasicStatusManager— internal status events accumulate during XML configuration loading without being consumed.TopicMessageType.getMetricsValue()— allocates a newStringon every call viavalue.toLowerCase().RemotingHelper.getRequestCodeDesc()/getResponseCodeDesc()— eagerly evaluatesString.valueOf(code)inMap.getOrDefault()even on cache hit.Motivation
JFR
settings=profileon a broker under steady-state send load reveals these three allocation sites as recurring per-RPC waste:InfoStatus,BodyEvent) accumulate inBasicStatusManagerretained set (~655 KiB) despite never being queried in production. AddingNopStatusListenersuppresses the callback overhead.TopicMessageType.getMetricsValue()is called once per send for metrics labeling. ThetoLowerCase()result is deterministic per enum constant — caching it in the constructor avoids ~1 String + 1 byte[] per RPC.RemotingHelper.getRequestCodeDesc()andgetResponseCodeDesc()are called per RPC response. Java eagerly evaluatesString.valueOf(code)as a method argument toMap.getOrDefault(), allocating a String even when the key exists in the map. A simpleget()+ null check eliminates this on the hot (cache-hit) path.Combined, these save ~200–300 bytes per RPC with zero behavioral change.
Describe the Solution Youd Like
Logback: Add
<statusListener class="ch.qos.logback.core.status.NopStatusListener"/>after<configuration>inrmq.broker.logback.xml.TopicMessageType: Cache
metricsValueas afinal Stringfield initialized in the enum constructor withLocale.ROOT. The getter returns the cached reference directly.RemotingHelper: Replace
REQUEST_CODE_MAP.getOrDefault(code, String.valueOf(code))with:Same pattern for
RESPONSE_CODE_MAP.Describe Alternatives Youve Considered
loggerContext.getStatusManager().clear()in code. Rejected because the XML-level listener is simpler and doesnt require broker startup changes.String.intern()for metrics values: Rejected due to global String table contention and lack of locale control.computeIfAbsentfor RemotingHelper: Overkill — the map is a staticMap<Integer, String>populated at class load time. A null check is simpler and avoids lambda allocation.Additional Context
PR: #10491
This PR is stacked on PR #10443 (AttributeKey statics) and will be rebased on
developafter #10443 merges to show only the 3 files above.