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]
