wang-jiahua opened a new issue, #10442:
URL: https://github.com/apache/rocketmq/issues/10442

   ### Before Creating the Enhancement Request
   
   - [x] I have confirmed that this should be classified as an enhancement 
rather than a bug/feature.
   
   
   ### Summary
   
   Replace per-message `HashMap` allocation in the properties encode/decode hot 
path with a compact `FlatPropertiesMap` backed by a flat `Object[]` array. Add 
ThreadLocal reuse for the map, StringBuilder, and char[] buffers. Intern 
high-frequency property keys and values to eliminate redundant String 
allocation.
   
   **Scope**: 8 files in `common/message`
   
   ### Motivation
   
   Heap dump analysis reveals:
   - **`HashMap$Node[]`** occupies **21.0%** of live heap — every message 
creates multiple HashMaps for properties parsing.
   - **`String`** occupies **8.1%** with only **21,873 distinct values out of 
100,000 sampled** — `"true"` duplicated 7,142 times, `"UNIQ_KEY"` 4,059 times, 
etc.
   - `byte[]` occupies **29.3%** — partly from backing arrays of duplicated 
small Strings.
   
   The `string2messageProperties` / `messageProperties2String` path is the 
single largest allocation hotspot on the broker send path.
   
   ### Describe the Solution You'd Like
   
   1. **`FlatPropertiesMap`** (new class): A compact `Map<String, String>` 
implementation using a flat `Object[]` (key-value pairs). Supports `reset()` 
for ThreadLocal reuse, `computeEncodedLength()`, and `encodeTo(ByteBuffer)` for 
zero-copy serialization.
   2. **`MessageDecoder`**: New `bytes2messageProperties(ByteBuffer)` method 
that parses properties byte-by-byte without `new String()` + `split()`. Uses 
`REUSABLE_PROPS_MAP` ThreadLocal for map reuse and `REUSABLE_SB` ThreadLocal 
for StringBuilder reuse.
   3. **`MessageConst`**: Add `STRING_INTERN_BY_LEN` — length-bucketed arrays 
for key interning. Known property keys (e.g., `KEYS`, `TAGS`, `WAIT`, 
`UNIQ_KEY`) are resolved to static constants by length + first-char matching.
   4. **`MessageDecoder`**: Add `VALUE_INTERN_BY_LEN` for common value 
interning (`"true"`, `"false"`, `"DefaultRegion"`, etc.).
   5. **`MessageClientIDSetter`**: ThreadLocal `char[]` buffer for 
`createUniqID` to avoid per-call allocation.
   6. **`MessageVersion`**: Replace `values()` iteration with direct `if-else` 
lookup.
   
   ### Describe Alternatives You've Considered
   
   - **`String.intern()` (JVM native)**: Risks polluting the JVM String table 
and has unpredictable GC interaction; length-bucketed array interning is 
deterministic and bounded.
   - **Keep `HashMap` but pool it**: Still pays the overhead of `HashMap.put()` 
internal Node allocation; `FlatPropertiesMap` avoids this entirely for the 
typical 8–12 property case.
   - **Protocol-level binary properties** (like Pulsar): Would require wire 
format changes; out of scope for a backward-compatible enhancement.
   
   ### Additional Context
   
   - `FlatPropertiesMap` is only used on the broker-internal decode→encode path 
within a single thread; it does not escape to user-facing APIs.
   - The send-path `string2messageProperties` in `MessageDecoder` returns 
`HashMap` (not `FlatPropertiesMap`) to maintain compatibility with downstream 
code that expects `HashMap`.
   - JFR-measured bytes/msg reduction: **-42.7%** from this change alone (from 
4,594 to 2,631 bytes/msg).


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to