科普篇之Java堆内缓存优化-StringCache的使用

试想如果当堆内缓存中存在大量重复的String字符串时(有限个),我们首先想到的是能不能把String缓存到一个Map中,当map中存在时就使用Map中的String,Map中不存在时就new一个String放进去,这正是String.intern()所实现的功能

当然如果你使用的是Long,Interger类型的主播id,可以自己实现String.intern()的功能,因为主播id是稀疏的而不是稠密的,所以无法利用Long,Interger类型自身的cache

以直播场景为例,在直播推荐系统中,主播id是主播的唯一标识,最初是一个32位整型,在公司大多数其它Java业务场景中都是使用Interger来表示,后来由于主播id分配完了,又统一升级到到64位整型,但在推荐业务场景中我们一直都是使用String来表示,美其名曰灵活,你变由你变,你怎么变我都不用变

在推荐服务内部会缓存很多i2i的相似词表,结构为Map<String,List<String>>,key为主播id,value的List中存放的是与key相似的主播id列表(用途就是比如你看过主播a,就会把与主播a相似的其它主播推荐给你),虽然只有3万量级的主播,但这样一个Map,往往会有1000多万个主播id,其中绝大部分是重复的主播id

比如:

aid1->aid2,aid3,aid4,aid5

aid2->aid4,aid5,aid6,aid10

aid3->aid1,aid2,aid9,aid10

当然这个结构是简化过的,一般每个aid还会有个Float类型的分数,用与在线召回时排序截断

如果我们有多个这种i2i相似词表,那么缓存中将会存在大几千万重复主播id,此时如果我们用String.intern()来优化,最终缓存中的主播id,将会由千万级下降到万级,并不会随着i2i词表的增加而增加,因为主播数是固定的,只需在正常加载词表时使用String.intern()即可

正常加载代码

使用String.intern()优化

在我们的场景中使用String.intern()优化i2i词表后通过对比垃圾回收后的内存占用,粗略估计减少了近6G的内存使用

优化前

优化后

自jdk7常量池已从永久代调整到堆区了,在jdk21下观察String常量池的Number of entries,Number of buckets大小,常量池可正常扩容(无需配置-XX:StringTableSize),常量池中的字符串可正常被垃级回收

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值