Java堆外内存的使用
作者:网络转载 发布时间:[ 2015/1/4 13:21:13 ] 推荐标签:Java 软件开发
堆外内存的使用
使用堆外内存与对象池都能减少GC的暂停时间,这是它们的共同点。生命周期短的可变对象,创建开销大,或者生命周期虽长但存在冗余的可变对象都比较适合使用对象池。生命周期适中,或者复杂的对象则比较适合由GC来进行处理。然而,中长生命周期的可变对象比较棘手了,堆外内存则正是它们的菜。
堆外内存的好处是:
可以扩展至更大的内存空间。比如超过1TB甚至比主存还大的空间。
理论上能减少GC暂停时间。
可以在进程间共享,减少JVM间的对象复制,使得JVM的分割部署更容易实现。
它的持久化存储可以支持快速重启,同时还能够在测试环境中重现生产数据。
站在系统设计的角度来看,使用堆外内存可以为你的设计提供更多可能。重要的提升并不在于性能,而是决定性的。
堆外内存及测试
高性能计算领域大的一个难点在于重现那些隐蔽的BUG,并证实问题已经得到修复。通过将输入事件及数据以持久化的形式存储到堆外内存中,你可以将你的关键系统变成一系列的复杂状态机。(简单的情况下只有一个状态机)。这样的话在测试环境便能够复现出生产环境出现的行为及性能问题了。
许多投行都通过这项技术来可靠地重现当天系统对某个事件的响应,并分析出该事件之所以这么处理的原因。更为重要的是,你能够立即证明线上的故障已经得到了解决,而不是发现一个问题后,寄希望于它是引发线上故障的根源。确定性的行为还伴随着确定性的性能。
你可以在测试环境中按照真实的时间来回放事件,由此得到的时延分布也必定是生产环境中所出现的。由于硬件的不同,一些系统的抖动可能难以复现,不过这在数据分析的角度而言已经相当接近真实的情况了。为了避免出现花一整天的时间来回话前的数据的情况,你还可以增加一个阈值,比方说,如果两个事件的间隔超过10ms的话你可以只等待10ms。这样你能够在一个小时内根据实际的时间来回放出的事件,来检查下你的改动是否对时延分布有所改善。
这样做是否损失了“一次编译,处处执行”的好处了?
一定程度上来讲是这样的,但其实的影响比你想像的要小得多。越接近处理器,你更依赖于处理器或者操作系统的行为。所幸的是,绝大多数系统使用的都是AMD/Intel的CPU,甚至是ARM处理器在底层上也越来越与这两家兼容了。操作系统之间也存在差别,因此相对于Windows而言,这项技术更适合在Linux系统上使用。如果你是在Mac OS X或者Windows上开发,然后生产环境是部署在Linux上的话,一点问题都没有了。我们在Higher Frequency Trading中也是这么做的。
使用堆外内存会引入什么新的问题
天下没有免费的午餐,堆外内存也不例外。大的问题在于你的数据结构变得有些别扭。要么是需要一个简单的数据结构以便于直接映射到堆外内存,要么使用复杂的数据结构并序列化及反序列化到内存中。很明显使用序列化的话会比较头疼且存在性能瓶颈。使用序列化比使用堆对象的性能还差。
在金融领域,许多高频率的数据都是扁平的简单结构,全部由基础类型组成,非常适合映射到堆外内存。然而,并非所有的应用程序都是这样的,可能会有一些嵌套得很深的数据结构,比如说图,你还不得不将这些对象缓存在堆上。
另外一个问题是JVM会制约到你对操作系统的使用。你不用再担心JVM会给系统造成过重的负载。使用堆外内存后,某些限制已经不复存在了,你可以使用比主存还大的数据结构,不过如果你这么做的话又得考虑一下使用的是什么磁盘子系统了。比如说,你肯定不会希望分页到一块只有80 IOPS(Input/Ouput Operations per Second,每秒的IO操作)的HDD硬盘上,好是IOPS能到80,000的SSD硬盘,当然了,1000x的话更好。
OpenHFT能做些什么?
OpenHFT包含许多类库,它们向你屏蔽了使用本地内存来存储数据的细节。这些数据结构都是持久化的,使用它们不会产生垃圾或者只有很少。使用了它的应用程序可以运行一整天也没有一次Minor GC.
Chronicle Queue——持久化的事件队列。支持同一台机器上多个JVM的并发写,以及多台机器间的并发读。微秒级的延迟,并能持续保持每秒上百万消息的吞吐量。
Chronicle Map——kv表的本地或持久化存储。它能在同一台机器的不同JVM间共享,数据是通过UDP或者TCP来复制的,并通过TCP来进行远程访问。微秒级的延迟,单台机器能保持每秒百万级的读写操作。
Thread Affinity ——将关键线程绑定到独立的CPU核或者逻辑CPU上,以减少系统抖动。抖动可以减小到原来的千分之一。
使用哪个API?
如果你需要记录每个事件的话 ——> Chronicle Queue
如果你只需要某个主键近的一条结果 ——> Chronicle Map
如果你更关心那20微秒的抖动的话 ——> Thread Affinity
总结
堆外内存是把双刃剑。它的价值你已经看到了,可以和别的实现可伸缩性的方案进行下比较。与在堆缓存或者消息队列,甚至是进程外的数据库中进行分区/分片相比,使用堆外内存要更为简单高效。不仅如此,以前用来提升性能的某些技巧也已经不再需要了。比如说,堆外内存可以支持操作系统的同步写,不再需要异步去执行了,那样还会面临数据丢失的风险。不过大的好处应该是启动时间了,生产环境下的系统的重启速度会大大缩短,映射1 TB的数据只需要10毫秒,同时你还能在测试环境按生产环境的顺序复现每一个事件,以还原线上现场。通过它你可以建立起一个可靠的质量体系。

sales@spasvo.com