简介:
通过对推荐权益业务的缓存改造,对比分析在本地缓存数据量较大的背景下,分别使用堆内内存与堆外内存实现缓存策略的接口性能表现。探索开源高性能缓存库OHC在推荐权益业务的适用性。
业务背景:
在会员权益推荐中,我们把推荐的权益信息放入本地缓存,通过Caffeine进行管理。随着权益数据及推荐渠道、运营位置的增多,推荐权益的基础配置量也快速累积。我们知道,本地缓存占用JVM内存,数据量过多会影响GC,造成GC频率、耗时增加,进而对系统性能产生直接影响。那么在本地缓存数据量多的情况下有没有其他解决方案呢?
优势:
使用堆外缓存OHC(Off-Heap Cache)的优势:
· 不占用JVM堆内存,摆脱GC影响
· 支持异步缓存加载机制
· 支持为每个缓存条目单独设置或使用默认的TTL(Time-To-Live,生存时间)/过期时间
· 能够在不需要额外线程的情况下进行缓存条目的淘汰和过期处理
· 具备管理大规模缓存内存的能力
· 通过分块实现方式,适合存储低开销的极小或小型缓存条目
实现过程:
一 接入OHC并序列化
OHC快速接入示例中,需要我们自定义序列化方式,Protobuf、Thrift、kryo、hessian自由选择。以下示例采用Protobuf序列化。
1) 引入POM依赖:
2) 构建堆外权益缓存:
二 实践及效果
- 数据准备:
首先看看本地配置JVM启动的几个关键指标:
-Xmx1g:、 -Xms1g设置JVM初始堆内存和最大堆内存为1GB。
-XX : +UseConcMarkSweepGC: 启用CMS垃圾回收器。
-XX : CMSInitiatingOccupancyFraction=70: 设置堆内存使用率达到70%时触发CMS垃圾回收
-XX : MaxDirectMemorySize 最大堆外内存
根据参数配置,我们可模拟构建10w+个权益对象,使权益内存到达700M(可以使用jol查询某个对象总共占用了多少内存空间),以此来触发CMS工作。
注:压测工具及数据均一致,使用Jmeter压测权益推荐接口,30s内10个线程持续请求。
- 内存及GC状态对比:
将本地内存切换为堆外内存后,为了更直观的对比两者的GC状态及接口性能,我们分别监控不使用缓存场景、使用堆内内存场景、使用堆外内存三组场景下的GC静息状态及压测状态下,heap内存和gc状态变化(GC次数及GC时长)。并输出压测报告。
· 无缓存
图 I无缓存内存图
图 II无缓存GC状态
项目启动时,基础类加载使用250M内存,GC再类加载时开始工作,FULL GC 0次,Minor GC 29次,每次约10ms。
· 使用堆内内存
图 III 堆内存静息时内存
图 IV 堆内存静息时GC
项目启动即开始加载权益缓存,内存使用750M开始触发GC工作,其中FULL GC50次,每次28ms,Minor GC 32次,每次11.5ms
图 V 堆内存压测时GC
项目压测时,可以看到GC开始加速工作,FULL GC次数达184次,每次耗时增加到88ms。
图 VI 堆内存压测报告
·使用OHC堆外内存
图 VII 堆外静息状态内存
图 VIII 堆外内存静息状态GC
项目启动堆内内存无变化,GC基本跟无缓存使用状态一致。
图 IX 堆外内存压测时GC
项目压测时,可以看到GC开始工作,FULL GC次数0次,Minor约6ms。
图 X 堆外内存压测报告
- 现象分析:
将本地缓存的大对象,移出到堆外缓存后,GC减少,接口性能提升2.2倍。
三 源码浅析
1)内存分配
点开put方法,发现核心OHCache接口提供了两种实现:
OHCacheLinkedImpl: 实现为每个条目单独分配堆外内存,最适合中型和大型条目。
OHCacheChunkedImpl:实现为每个散列段作为一个整体分配堆外内存,并且适用于小条目。(实验性的,不做关注)
OHCacheLinkedImpl的主要工作流程如下:
·计算key的hash值,根据hash值计算段号,确定其所处的OffHeapLinkedMap
·从OffHeapLinkedMap中获取该键值对的堆外内存地址(指针)
·对于get操作,从指针所指向的堆外内存读取byte[],把byte[]反序列化成对象
·对于put操作,把对象序列化成byte[],并写入指针所指向的堆外内存
2)内存释放
释放内存对应也有两种方法,一个用的是sun.misc.Unsafe的freeMemory,一个用的是com.sun.jna.Native的free,可以肯定的是,都需要我们手动释放。如果写C可能觉得这是天经地义的,毕竟你向操作系统借了一块内存,那肯定要还的。但是还内存这一步,Java程序员平时都是全权交由JVM管理的。
四 缓存使用建议
1)当本地缓存对垃圾回收(GC)造成显著影响时,可以采用堆外缓存(OHC)来降低其对GC的负担;
2))由于OHC将键(key)和值(value)序列化为字节数组后存储在堆外内存中,因此选择一个高效的序列化工具对于优化性能至关重要;
3) 为了减少缓存对象的体积,可以缩短缓存的JSON字符串中的字段名,这不仅减少了数据存储和传输量,还有助于增强数据的隐私性;
4)OHC可替代Caffeine等堆内存中间件的主要特性,至于你的项目是否需要使用堆外内存,还要取决于你的堆内内存占用量是否对GC产生了影响。
五 结果及影响
高效利用服务器内存,对本地需缓存较大数据量的业务场景提供新的解决方案。当本地缓存对垃圾回收(GC)造成显著影响时,可以采用堆外缓存来降低GC负担。如上文所示,堆内内存达到GC触发阈值的条件时,将堆内内存移出至堆外,推荐权益的接口性能提升了2.2倍;
到此这篇webflux使用堆外内存(webflux databuffer)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/rfx/62253.html