Alibaba FFI -- 跨语言编程的探索

简介: 跨语言编程时现代程序语言中非常重要的一个方向,也被广泛应用于复杂的设计与实现中。 

跨语言编程是现代程序语言中非常重要的一个方向,也被广泛应用于复杂系统的设计与实现中。本文是 GIAC 2021(全球互联网架构大会) 中关于 Alibaba FFI -- '跨语言编程的探索' 主题分享的内容整理。作者:董登辉、顾天晓,来自阿里云智能基础软件部 JVM 团队

E7A2381.JPG

背景

前言

无疑,Java 是目前工业界最流行的应用编程语言之一。除了主流实现上(OpenJDK Hotspot)不俗的性能表现和成熟的研发生态(Spring 等)外,其成功的背后离不开语言本身较低(相比于 C/C++)的学习门槛。一个初学者可以利用现有的项目脚手架快速地搭建出一个初具规模的应用程序,但也因此许多 Java 程序员对程序底层的执行原理并不熟悉。本文将探讨一个在大部分 Java 相关的研发工作中不太涉及到的技术 -- 跨语言编程。

回想起多年前第一次用 Java 在控制台上打印出 “Hello World” 时,出于好奇便翻阅 JDK 源码想一探实现的究竟 (在 C 语言中我们可以使用 printf 函数,而 printf 在具体实现上又依赖操作系统的接口),再一次次跳转后最终停留在了一个“看不到实现”的 native 方法上,额。。。然后就没有然后了。

我想有不少 Java 初级程序员对 native 方法的调用机制仍一知半解,毕竟在大部分研发工作中,我们直接实现一个自定义 native 方法的需求并不多,简单来说 native 方法是 Java 进行跨语言调用的接口,这也是 Java Native Interface 规范的一部分。

image.png

image.gif

Java 跨语言编程技术的应用场景

常见场景

在介绍 Java 跨语言编程技术之前,首先简单地分析下实际编程过程中需要使用跨语言编程技术的场景,在这里我罗列了以下四个场景:

1、依赖字节码不支持的能力

换个角度看,目前标准的字节码提供了哪些能力呢?根据 Spec 规范,已有的字节码可以实现创建 Java 对象、访问对象字段和方法、常规计算(加减乘除与或非等)、比较、跳转以及异常、锁操作等等,但是像前言中提到的打印字符串到控制台这样的高阶功能,字节码并不直接支持,此外,像获取当前时间,分配堆外内存以及图形渲染等等字节码也都不支持,我们很难写出一个纯粹的 Java 方法(组合这些字节码)来实现这类能力,因为这些功能往往需要和系统资源产生交互。在这些场景下,我们就需要使用跨语言编程技术通过其他语言的实现来集成这些功能。

2、使用系统级语言(C、C++、Assembly)实现系统的关键路径

不需要显示释放对象是 Java 语言学习门槛低的原因之一,但因此也引入了 GC 的机制来清理程序中不再需要的对象。在主流 JVM 实现中,GC 会使得应用整体暂停,影响系统整体性能,包括响应与吞吐。

所以相对于 C/C++ ,Java 虽然减轻了程序员的研发负担,提高了产品研发效率,但也引入了运行时的开销。(Software engineering is largely the art of balancing competing trade-offs. )

当系统关键路径上的核心部分(比如一些复杂算法)使用 Java 实现会出现性能不稳定的情况下,可以尝试使用相对底层的编程语言来实现这部分逻辑,以达到性能稳定以及较低的资源消耗目的。

3、其他语言实现调用 Java

这个场景可能给大部分 Java 程序员的第一感觉是几乎没有遇到过,但事实上我们几乎每天都在经历这样的场景。

举个例子:通过 java <Main-Class> 跑一个 Java 程序就会经过 C 语言调用 Java 语言的过程,后文还会对此做提及。

4、历史遗留库

公司内部或者开源实现中存在一些 C/C++ 写的高性能库,用 Java 重写以及后期维护的成本非常大。当 Java 应用需要使用这些库提供的能力时,我们需要借助跨语言编程技术来复用。

Alibaba Grape

再简单谈谈阿里内部的一个场景:Alibaba Grape 项目,也是在跨语言编程技术方向上与我们团队合作的第一个业务方。

Grape 本身是一个并行图计算框架的开源项目(相关论文获得了 ACM SIGMOD Best Paper Award),主要使用 C++ 编写,工程实现上应用了大量的模板特性。对 Grape 项目感兴趣的同学可以参考 Github 上的相关文档,这里不再详细介绍。

该项目在内部应用中,存在很多使用 Java 作为主要编程语言的业务方,因此需要开发人员把 Grape 库封装成 Java SDK 供上层的 Java 应用调用。在实践过程中,遇到的两个显著问题:

  • 封装 SDK 的工作非常繁琐,尤其对于像 Grape 这样依赖模板的库,在初期基于手动的封装操作经常出错
  • 运行时性能远低于 C++ 应用

为了解决这些问题,两个团队展开了合作,Alibaba FFI 项目正式开始演进,该项目的实现目前也主要针对 Java 调用 C++ 场景。

1.png

Java 跨语言编程技术

下面介绍一些在工业界中相对成熟、应用较多的 Java 跨语言调用技术。

Java Native Interface

谈到 Java 跨语言编程,首先不得不提的就是 Java Native Interface,简称 JNI。后面提到的 JNA/ JNR、JavaCPP 技术都会依赖 JNI。首先,通过两个例子来简单回顾一下。

控制台输出的例子

Plain Text

自动换行

xxxxxxxxx

1

System.out.println("hello ffi");

通过 System.out 我们可以快速地实现控制台输出功能,我相信会有不少好奇的同学会关心这个调用到底是如何实现输出功能的,翻阅源码后,我们最终会看见这样一个 native 方法:

private native void writeBytes(byte b[], int off, int len, boolean append) throws
IOException;

(该方法由 JDK 实现,具体实现可以参考这里。)

那么我们是否可以自己实现这样的功能呢?答案是肯定的,大致步骤如下(省略了一些细节):a. 首先我们定义一个 Java native 方法,需要使用 native 关键字,同时不提供具体的实现(native 方法可以被重载)

static native void myHelloFFI();

b. 通过 javah 或者 javac -h (JDK 10)命令生成后续步骤依赖的头文件(该头文件可以被 C 或者 C++ 程序使用)

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloFFI */


#ifndef _Included_HelloFFI
#define _Included_HelloFFI
#ifdef __c
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值