You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenJDK 11中ConcurrentHashMap putVal方法高命中次数原因及底层流程解析请求

Why Does ConcurrentHashMap.putVal() Get Called 129+ Times for Just One put()?

Great question—let’s unpack this, because ConcurrentHashMap’s internals are already complex, and debugging tools add their own hidden behavior that can throw off your counts.

First: The Normal put() → putVal() Flow (Simplified)

First, let’s clear up the baseline: your manual put() calls shouldn’t be triggering hundreds of putVal() invocations on their own. Here’s the stripped-down version of what happens when you call map.put(key, value):

  • The public put() method is just a thin wrapper that calls putVal(key, value, false) (the false means we allow overwriting existing values).
  • Step 1: Hash Calculation: Compute the hash of the key to find its position in the underlying table array.
  • Step 2: Lazy Initialization: If the table hasn’t been created yet (first put()), initialize it using CAS operations to avoid locks.
  • Step 3: Bucket Check:
    • If the target bucket is empty, use CAS to insert the new node directly—done!
    • If the bucket’s head is a ForwardingNode (signaling an ongoing resize), help with the resize and retry the put (this might trigger an extra putVal() call, but only once or twice per put).
  • Step 4: Handle Collisions:
    • If the bucket uses a linked list, traverse it to find the key: replace the value if found, or add a new node to the end (locking the bucket briefly with synchronized).
    • If the bucket is a red-black tree, insert/update the node using tree logic (again, locking the bucket).
  • Step 5: Resize Check: If the bucket’s size hits the threshold, convert the list to a tree or trigger a full resize.

Under normal circumstances, one put() will trigger 1-3 putVal() calls at most (only extra calls if resizing requires retries).

So Why Are You Seeing 129+ Calls?

The massive number of putVal() hits you’re seeing is almost entirely due to IntelliJ’s debugger itself, not your code. Here’s why:

  • The debugger runs an agent (the debugger-agent.jar in your launch command) inside your JVM process. This agent does a lot of background work to track variables, display data in the Variables window, and collect performance metrics for the Overhead tab.
  • This agent likely uses ConcurrentHashMap internally for its own bookkeeping—every time it puts data into its own maps, that triggers putVal() calls that get counted in your method stats.
  • Additionally, when the debugger displays the state of your map2 instance (e.g., showing entries in the Variables window), it may trigger internal ConcurrentHashMap methods that indirectly lead to extra putVal() invocations (though this is less likely than the agent’s own usage).
  • Even if you only call put() once, the debugger’s internal activity is responsible for the other 128+ putVal() hits. The difference between 129 and 131 calls is just the two extra put()s in your original code—those add 2 real calls on top of the debugger’s noise.

How to See the Real Call Count

If you want to verify the actual number of putVal() calls from your code:

  • Run the program without debugging (use the "Run" button instead of "Debug").
  • Add manual logging or use a lightweight profiling tool (like JDK’s built-in jstack or minimal-overhead profilers) to track method invocations, avoiding IntelliJ’s heavy debugging agent.

内容的提问来源于stack exchange,提问作者ravibeli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 02:47:50