一招解决游戏内存不足的神器Caffeine

时间:2024-06-01 09:56:48

问题定位:

最近奉命解决游戏中一直在的内存不够的问题,最终定位到底层的缓存机制使用的是google提供的ConcurrentLinkedHashMap做数据缓存的。

简单介绍下ConcurrentLinkedHashMap,这是一个基于LRU策略的缓存,说白了就是热点数据缓存,支持设置最大缓存个数,监控到缓存数量超过最大值后会依据权重策略让数据过期。

分析:

根据ConcurrentLinkedHashMap的特性,我们这边推测出玩家数据在被加载放到缓存后,如果缓存没有超过设置的最大值,则这些数据会一直放在缓存中;合理上来说这些没被使用的数据,在空闲一定时间后是可以被清除的,而不用一直占据在缓存中。

然而ConcurrentLinkedHashMap并没有在数据空闲一定时间后自动清空数据的机制,因而想要解决这个缓存问题,必须使用一个既支持基于缓存个数大小回收,又支持基于空闲时间回收的第三方组件,而在多番了解和数据查证后最终选择了Caffeine。

Caffeine是什么

Caffeine 是一个高性能的 Java 缓存库,缓存和 Map 之间的一个根本区别在于缓存可以根据回收策略回收存储的数据。此策略直接影响缓存的命中率 ,而缓存命中率额也是缓存库的一个重要特征。Caffeine 因使用 Window TinyLfu 回收策略,提供了一个近乎最佳的命中率。

我们可以看到官网提供的几张性能截图

场景1:8个线程读,100%的读操作

一招解决游戏内存不足的神器Caffeine

场景二:6个线程读,2个线程写,也就是75%的读操作,25%的写操作

一招解决游戏内存不足的神器Caffeine

场景三:8个线程写,100%的写操作

一招解决游戏内存不足的神器Caffeine

可以清楚的看到Caffeine效率明显的高于其他缓存。

将Caffeine替换掉ConcurrentLinkedHashMap,接下来就是要验证下,是否支持基于空闲时间回收,进而解决我们游戏中的内存问题了。

Caffeine和ConcurrentLinkedHashMap的使用对比

操作流程:本地起两个服,一个缓存底层使用的是ConcurrentLinkedHashMap,另一个底层使用的是Caffeine,Caffeine服设置的数据操作空闲时间是1分钟,起服后分别使用jvisualvm记录起始内存曲线,截图保留,之后启动2000个机器人模拟登陆,登陆后再过1分钟分别手动调用FullGC,查看jvisualvm记录的内存曲线,并查看堆dump记录。

【ConcurrentLinkedHashMap服】起始内存

一招解决游戏内存不足的神器Caffeine

【ConcurrentLinkedHashMap服】2000个机器人模拟登陆后下线,并手动调用FullGC后查看内存曲线

一招解决游戏内存不足的神器Caffeine

我们可以看到堆内存并没有回到原来的曲线。

查看堆dump记录

一招解决游戏内存不足的神器Caffeine

可以看到该对象的实例数没有下降到0。

【Caffeine服】起始内存

一招解决游戏内存不足的神器Caffeine

【Caffeine服】2000个机器人模拟登陆后下线,并手动调用FullGC后查看内存曲线

一招解决游戏内存不足的神器Caffeine

这里看到内存曲线回到了起服时的模样,再查看堆dump记录

一招解决游戏内存不足的神器Caffeine

可以看到,对象实例个数也被回收了,那么这里剩余的实例个数1是怎么回事,这里涉及到Caffeine的原理,留个悬念,等下篇文章再讲解,有兴趣关注一波,后期准备写个Caffeine系列教程。

通过以上对比,可以得出Caffeine具备“自动”清空缓存过期数据的机制,并且可以解决我们游戏中一直存在的内存问题。

总结

目前对Caffeine的替换已经提交到线上运行了,由于目前网上相关教程偏少,因而后续准备写一系列Caffeine相关的文章进行解密,有兴趣的关注一波。

一招解决游戏内存不足的神器Caffeine