-
Notifications
You must be signed in to change notification settings - Fork 8.1k
优化CtSph#lookProcessChain 创建拦截链方法(减少第一次创建拦截链持有锁占用时间,提升rt,减少gc压力) #3513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
我理解这边主要讨论了 chainMap 使用 HashMap 还是 ConcurrentHashMap 的问题。首先我们明确下使用的场景,chainMap 的使用场景主要分成两个阶段,一个是初始化阶段,一个是运行阶段。在初始化阶段中,相较于性能,我们会更关注准确性,包括对于数量的控制等;在运行阶段,则更关注性能。 |
我的表达可能不清晰,我的意思是在 lookProcessChain 方法的初始化过程中,即使chainMap使用hashMap,也不需要使用 copyOnWrite 来频繁拷贝 map,因为 synchronized 已经保证了线程安全和可见性,直接通过 put 操作即可。此外,我也意识到关于 HashMap 和 ConcurrentHashMap 的使用,特别是在 get 操作上的性能差异,感谢你的提醒,之前确实没考虑到这一层。 |
lookProcessChain 方法中的第一个 get 操作不在 synchronized 的范围内哈,还是会存在并发 get 和 put 的情况 |
是的,第一个get操作不在synchronized范围内,但是他做了一层double check。后面synchronized代码块中没有必要对chainMap进行copyOnWrite的操作。 |
有道理,即便有 resize 导致的脏读也是读到 null ,在有 double check 的情况下后面的 copyOnWrite 操作确实没有必要 |
大佬请教一下两个问题:
|
之前使用 CopyOnWrite 是为了保证 resetChainMap、getChainMap 和 entrySize 方法操作 map 时的可见性。考虑到 resetChainMap 和 getChainMap 是 default 方法,仅在包内调用,我的pr中对entrySize 通过 synchronized 保证可见性,因此去掉了 CopyOnWrite。 |
Uh oh!
There was an error while loading. Please reload this page.
原逻辑:
chainMap 是一个 volatile 修饰的 Map。首次获取 ProcessorSlotChain 时,如果为空,旧逻辑会进入同步块,new 一个新的 map,并通过 putAll 拷贝原有数据后再新增,最终整体替换 chainMap,利用 volatile 替换引用实现可见性。
优化后:
新逻辑为在同步块中直接put。(synchronized本身保证可见性)
好处:
1: 提高rt:
避免在高并发下频繁进行 putAll 拷贝,特别是在 map 较大(如含有数百个资源)的情况下,putAll花费的时间越多,优化可以减少锁占用时间,从而提高rt。
2: 减少GC压力:
直接put无需频繁创建大量短生命周期的临时 map,降低内存占用和 GC 负担。
理由:
看了整个代码后,发现真正使用chainMap的地方也只有lookProcessChain这一个方法,其余的方法比如resetChainMap、getChainMap是default修饰的,仅仅用于测试调用,只能在当前包调用,所以这里我没有加synchronized。而entrySize方法是一个public修饰的方法,为了保证其可见性,我使用了synchronized关键字。
synchronized关键字本身通过内存屏障保证了可见性,因此chainMap不需要加volatile关键字修饰。
类似的实现比如java中的Collections.synchronizedMap()。

后续思考:
当资源数限制(如 6000)放开后,或者可以允许超一点点去追求极致的性能的话,代码还可以进行如下优化:
chainMap使用ConcurrentHashMap,不需要显示进行加锁,直接采用ConcurrentHashMap#computeIfAbsent方法,这样锁的粒度更小,性能更好。
The text was updated successfully, but these errors were encountered: