一、FFI 的本质是什么?
FFI(Foreign Function Interface)的本质是:
让一种语言能够调用另一种语言的函数或使用其数据结构的机制。
换句话说,FFI 是跨语言通信的“共同语言”,是一套用来让不同编程语言互相理解、互相调用的接口规范。
核心目的只有一个:
让语言 A 能像调用自己本地函数一样,去调用语言 B 的函数。
FFI 是一种“技术能力”,而不是某种语言的专属特性。
任何语言都有可能支持 FFI。
二、用类比帮助理解:FFI 就像国际翻译官
假设:
- Java = 中国人
- C/C++ = 美国人
- Python = 日本人
- Rust = 德国人
这些语言各自有自己的语法、类型系统、运行时模型,就像不同国家有不同语言。
FFI 的角色就像:
一个同时懂多国语言的翻译官,让他们彼此沟通。
例如:
- Java 想调用 C 函数:JNI = Java 世界的翻译官
- Python 想使用 C 库:ctypes / cffi = Python 世界的翻译官
- Rust 想调用 C 接口:Rust FFI = Rust 世界的翻译官
无论哪种语言,只要有一个 FFI,就可以与其他语言通信。
三、FFI 在技术层面是怎么运作的?
从操作系统的角度看:
编程语言最终都运行在操作系统和 CPU 上。
只要能调用动态库函数(.so / .dll),就能实现跨语言调用。
FFI 主要做三件事:
1. 函数调用协议统一(ABI 统一)
不同语言的函数,调用方式可能不一样,例如:
- 参数如何入栈
- 哪些寄存器保存什么数据
- 返回值如何传递
- 对齐方式
- 字符串格式
- 内存由谁释放
FFI 要解决的第一件事,就是让语言 A 调用语言 B 的函数时:
双方使用一致的 ABI(Application Binary Interface)。
ABI 就是机器级别的“通信协议”。
2. 数据类型转换
例如:
Java 的 int = 32 位
C 的 int = 在大多数平台上也是 32 位
→ 可以直接映射
但 Java 的 String ≠ C 的 char*
Java 的对象 ≠ C 的结构体
FFI 负责定义:
- 哪些类型能直接映射
- 哪些需要“包装”或“桥接”
- 如何管理内存释放
3. 运行时隔离与安全
FFI 还需要保证:
- 访问非法内存时能控制风险
- 语言运行时(GC、异常机制)不被破坏
- 跨线程、跨进程的调用是安全的
- 不互相影响生命周期
这些都是跨语言调用的重点难题。
似乎类似于网络的远程接口交互,不同的语言,但是是通过共用的网络协议、接口协议来进行的。
四、FFI 在 JVM/JNI 中的体现
JNI 实际上就是 JVM 给 Java 提供的 专属 FFI。
它提供:
- 函数调用协议:如何从 Java 调到 C/C++
- 数据类型映射:jint、jstring、jobject
- 运行时交互:Java 对象如何从 native 世界被引用
- GC 保护机制:LocalRef / GlobalRef
- 线程管理:AttachCurrentThread
- 异常传递机制:ExceptionOccurred / ThrowNew
换句话说:
**JNI 是 JVM 定义的 FFI 标准,C/C++ 是只是实现语言。
JNI 定义规则 → JVM 实现规则 → Java 才能调用 C/C++。**
五、FFI 的典型用途(不限于 Java)
几乎所有现代语言都依赖 FFI:
Java
- JNI = FFI
- 用来调用 C/C++、OpenGL、音视频库、操作系统 API
Python
- ctypes / cffi = FFI
- 调用 C 的 TensorFlow/PyTorch 核心组件
Rust
- Rust FFI ←→ C 是第一公民
- 调用 C 库、或被 C/C++ 调用
Go
- cgo = FFI
- 调用 C 库、OpenSSL、系统调用
JavaScript(Node.js)
- N-API / FFI
- 调用 C++ addon
FFI 让语言能够复用不同语言的生态,避免重复造轮子。
六、常见误解:
❌ 误解 1:Java 能调用 C/C++ 是因为 JVM 用 C++ 写的
正确:Java 能调用 C/C++ 是因为 JVM 实现了 JNI(FFI 规范),与实现语言无关。
❌ 误解 2:FFI = 调 C/C++
正确:
FFI 可以连接任何语言:Python → Java、Rust → C、Go → C++、JS → Rust……
❌ 误解 3:FFI 很简单
正确:
FFI 是跨语言互操作中最复杂、最底层的机制之一,涉及:
- ABI
- GC
- 线程模型
- 异常机制
- 生命周期管理
七、一句话总结
理解 FFI 的关键:
FFI 是跨语言调用的通用桥梁,不是某种语言自带的特权能力。 JNI 是 JVM 的 FFI 规范,而不是 JVM 使用了 C++ 才能调用 C++。
可以相关调用的本质:
建立起来可以信息交互的通路,只要可以相互发信息,便可以认为是一种调用方式。
所谓不同的语言并非是鸿沟,在操作系统维度,在硬件-cpu、寄存器、内存等角度上看,并没有什么区别,都是数据、信息、信号、01.
也就是有了彼此认识的通讯协议、彼此联通的通讯通路。