在高频交易畛域,自动化应用程序每天须要解决数亿个市场交易信号,并在寰球各交易所之间发送成千上万的订单。
为了放弃竞争力,响应工夫必须始终保持在微秒级,特地是在产生相似“黑天鹅”事件的异样高峰期。
在一个典型的架构中,金融市场的交易信号被转换成外部的市场数据格式(应用各种协定,如 TCP/IP、UDP 组播和多种格局,如二进制、SBE、JSON、FIX 等)。
这些规范化的音讯被发送到算法服务器、统计引擎、用户界面、日志服务器和各种类型的数据库(内存数据库、物理数据库、分布式数据库)。
这条门路上的任何一个提早都有可能带来严重后果(比方基于旧价格做出战略决策或订单达到交易市场的工夫太迟),并为此付出惨重代价。
为了放慢至关重要的几微秒,大多数券商投入了低廉的硬件:服务器组装备了超频液冷的 CPU(在 2020 年,你能够买到单台装备了 56 核 5.6 GHz CPU 和 1 TB 内存的服务器)、凑近交易所的数据中心、纳秒级高端网络交换机、海底专线(Hibernian Express 是一家次要的供应商),甚至是微波网络。
咱们常常看到高度定制的能够绕过操作系统的 Linux 内核,数据能够间接从网卡“跳转”到应用程序、IPC(过程间通信),甚至是 FPGA(可编程单用处芯片)。
在编程语言方面,C++ 仿佛是服务器端应用程序的人造竞争者:它速度快,与机器码十分靠近,而且一旦针对指标平台进行编译,就能够提供恒定的解决工夫。
然而,咱们做了一个不一样的抉择。
在过来的 14 年里,咱们始终在用 Java 开发外汇算法交易系统,并应用了很棒但价格实惠的硬件。
因为团队规模小,资源无限,技术能力强的开发人员难找,所以应用 Java 意味着咱们能够疾速地改良软件性能,因为 Java 生态系统比 C 语言生态系统的公布速度更快。上午探讨性能改良,下午就能够实现、测试并公布到生产环境。
与那些须要几周甚至几个月能力公布更新的大公司相比,这是一个要害的劣势。在高频交易畛域,一个破绽能够在几秒钟内抹掉一整年的利润,所以咱们不打算在品质上做任何斗争。咱们搭建了一个严格的麻利开发环境,包含 Jenkins、Maven、单元测试、夜晚构建和 Jira,应用了很多开源库和我的项目。
应用 Java,开发人员能够专一于直观的面向对象业务逻辑,而不是浪费时间去调试一些艰涩的内存外围转储或治理 C++ 指针。而且,因为 Java 弱小的内存治理能力,即便是高级程序员也能够在第一天退出我的项目时为零碎带来价值,而且危险很小。
有了良好的设计模式和洁净的编码习惯,Java 的速度可与 C++ 相媲美。
例如,Java 会优化和编译在利用程序运行期间察看到的最佳门路,但 C++ 会事后编译所有货色,因而即便未被应用的办法也会成为可执行二进制文件的一部分。
然而,Java 有一个问题,它让 Java 成为一门弱小且令人青睐的编程语言,但也成了 Java 的毛病之一(至多对于微秒级应用程序来说)——Java 虚拟机(JVM):
- Java 在运行过程中编译代码(JIT),这意味着当它第一次运行某些代码时,会有编译提早。
- Java 治理内存的形式是在“堆”空间中分配内存块。每隔一段时间,它就会清理空间,移除旧对象,为新对象腾出空间。次要的问题是,为了进行精确的计数,应用程序线程须要临时“解冻”。这个过程称为垃圾回收(GC)。
GC 是低提早应用程序开发人员可能会放弃 Java 的次要起因。
市场上有一些可用的 Java 虚拟机。
最常见的是 Oracle Hotspot JVM,它在 Java 社区中被宽泛应用,次要是一些历史起因。
对于十分刻薄的应用程序,有一个很棒的代替计划,也就是 Azul Systems 的 Zing。
Zing 是规范 Oracle Hotspot JVM 的一个弱小的替代品。Zing 解决了 GC 进展和 JIT 编译问题。
接下来,让咱们来钻研一下 Java 的一些固有问题和可能的解决方案。
理解 Java 的 JIT 编译器
像 C++ 这样的编程语言被称为编译型语言,因为公布的代码齐全是二进制的,能够间接在 CPU 上执行。
PHP 或 Perl 被称为解释型语言,因为解释器 (装置在指标机器上) 会在运行时编译每一行代码。
Java 介于两者之间,它将代码编译成 Java 字节码,并在必要时再将其编译成二进制的。
Java 不在启动时编译代码的起因与后续的性能优化无关。通过观察利用程序运行并剖析实时办法调用和类初始化状况,Java 对常常被调用的代码局部进行编译。它甚至可能会依据教训做出一些假如(某些代码永远不会被调用,或者某个对象始终是一个字符串)。
编译过的代码执行速度十分快,但有三个毛病:
- 一个办法须要被调用肯定次数能力达到编译阈值,而后能力被编译和优化(这个阈值是可配置的,通常在 10000 次左右)。在此之前,未优化的代码不会“全速”运行。在更快的编译和高质量的编译之间存在折衷(如果假如是谬误的,就会产生编译老本)。
- 当 Java 应用程序重新启动时,咱们又回到了终点,必须期待再次达到阈值。
- 有些应用程序有一些不常被调用但很要害的办法,这些办法只会被调用几次,但在被调用时须要十分快。
Zing 通过让它的 JVM“保留”已编译的办法和类的状态(也就是所谓的 profile)来解决这些问题。这个独特的性能叫做 ReadyNow,也就是说 Java 应用程序能够始终以最佳速度运行,即便是在重启之后。
当你应用已有的 profile 重新启动应用程序,Azul JVM 会立刻发出以前的决策并间接编译重要的办法,以解决 Java 的预热问题。
此外,你也能够在开发环境中构建一个 profile 来模仿生产行为。优化后的 profile 能部署到生产环境中,并晓得所有要害门路都曾经过编译和优化。
下图显示了交易应用程序 (在模仿环境中) 的最大提早。
Hotspot JVM 的延时峰值是不言而喻的,而 Zing 的延时放弃得相当稳固。
百分比散布表明,在 1% 的工夫内,Hotspot JVM 产生的提早是 Zing JVM 的 16 倍。
解决垃圾回收进展问题
第二个问题是在垃圾回收期间,整个应用程序可能会进展几毫秒到几秒钟(提早会随着代码复杂性和堆大小的减少而减少),更蹩脚的是,你无法控制这种状况何时产生。
尽管对很多 Java 应用程序来说,暂停应用程序几毫秒甚至几秒是能够承受的,但对于低提早应用程序来说,这是一场劫难,无论是在汽车、航空航天、医疗还是金融畛域。
GC 影响对于 Java 开发人员来说是一个很大话题,Full GC 通常也叫作“进行世界的进展(stop-the-world)”,因为它会解冻整个应用程序。
多年来,有很多 GC 算法都试图升高吞吐量 (有多少 CPU 工夫用于利用程序逻辑执行而不是垃圾回收) 和 GC 进展(我能够暂停应用程序多长时间)。
从 Java 9 公布以来,G1 始终是默认的垃圾回收器,其次要思维是依据用户提供的工夫指标对 GC 进展进行划分。它通常提供较短的进展工夫,但以升高吞吐量为代价。此外,进展工夫随着堆的大小而减少。
Java 提供了大量的设置参数,从堆大小到回收算法以及调配给 GC 的线程数。因而,Java 应用程序通常会配置大量的参数:
很多开发人员通过各种技术来防止 GC。最次要的是,如果咱们少创立一些对象,那么后续要革除的对象就越少。
一种古老的 (依然在应用) 技术是应用对象池。例如,数据库连接池能够保留 10 个曾经关上的数据库连贯,以便在须要时应用。
多线程通常须要锁,这会导致同步提早和进展(特地是当它们共享资源时)。一种风行的形式是应用环形缓冲队列零碎,多个线程能够在一个无锁的环境中(请参考 disruptor)进行读写操作。
一些专家甚至处于无奈而抉择齐全笼罩 Java 的内存管理机制,由本人来治理内存调配,这尽管解决了问题,但也带来了更多的复杂性和危险。
因而,咱们须要思考应用其余 JVM,于是咱们决定尝试 Azul Zing JVM。
很快,咱们就可能在简直无进展的状况下实现很高的吞吐量。
这是因为 Zing 应用了一个叫作 C4(Continuously Concurrent Compacting Collector,间断并发压缩回收器)的垃圾回收器,它能够进行无进展的垃圾回收,而不论 Java 堆有多大(能够达到 8TB)。
这是通过在利用程序运行时并发映射和压缩内存来实现的。
此外,它不须要批改代码,而且提早和速度方面的改良都是开箱即用的,不须要进行繁冗的配置。
Java 程序员能够享受到两方面的益处:Java 的简略性 (不须要放心创立太多的新对象) 和 Zing 的底层性能,容许零碎中呈现高度可预测的提早。
GCeasy 提供了通用 GC 日志分析器,咱们能够在实在的主动交易应用程序 (在模仿环境中) 中疾速地对 JVM 进行比拟。
在咱们的应用程序中,应用 Zing 的 GC 大概比应用规范 Oracle Hotspot JVM 的 GC 快 180 倍。
更令人印象粗浅的是,GC 进展通常对应于理论的应用程序进展工夫,而 Zing 的 GC 通常是并行产生的,理论的进展很少,甚至没有进展。
总之,在享受 Java 的简略和个性的同时,依然有可能实现高性能和低提早。C++ 个别用于开发特定的底层组件,如驱动程序、数据库、编译器和操作系统,但大多数现实生活中的应用程序能够应用 Java 开发,甚至是要求很高的应用程序。
这就是为什么 Java 是排名第一的编程语言(依据 Oracle 的说法),并领有数百万开发者,在全世界有超过 510 亿个 Java 虚拟机。
举荐浏览
为什么阿里巴巴的程序员成长速度这么快,看完他们的内部资料我懂了
程序员达到 50W 年薪所须要具备的常识体系。
—小时解读并发编程三大个性
对于【暴力递归算法】你所不晓得的思路
看完三件事❤️
如果你感觉这篇内容对你还蛮有帮忙,我想邀请你帮我三个小忙:
点赞,转发,有你们的『点赞和评论』,才是我发明的能源。
关注公众号『Java 斗帝』,不定期分享原创常识。
同时能够期待后续文章 ing????