混合代码(本机、托管):它如何(技术上)互操作?

Mixed Code (native, managed): how does it (technically) interoperate?

我基本上理解托管代码和本机代码的概念及其 difference。但是他们之间如何在技术上进行交流呢?想象一下下面的例子:

我得到了一些针对特定平台编译的静态或动态 C++ 库。现在我写了一个 Java 程序。在这段代码中,我使用 'native' 关键字调用库函数。我用字节码构建一个 jar 文件,c++ 库文件将保持独立。结果将不再与平台无关。

  1. 但是 java 程序如何知道调用的本机方法是否存在?

  2. 整个程序代码是如何在运行时执行的?我知道字节码将用 JIT 解释或编译。

  3. 这一切如何适应沙盒范式?本机代码是否也在沙箱内执行?

  4. 是不是因为(java和c++)代码最后都是机器码?

也许这是个愚蠢的问题。但我一直想知道...

编辑:我得到了 3 个好的答案。真的无法决定哪个对我帮助最大。但是我会将这个问题标记为已回答以从我这边结束这个话题。

这取决于平台。在 Linux、Solaris 等平台上,JRE 使用 dlopen。在 Windows 上,它使用 LoadLibraryExGetProcAddress。如果 JRE 在解释模式下是 运行,它会调用该函数;在编译模式下,它将 Java 字节码编译成调用该函数的本机代码。

在我熟悉的所有JRE上,你不能直接调用静态库中的本地函数;动态库中只有一个。

本机代码不必局限于单一平台;如果它是标准 C,您可能可以使用交叉编译器为每个 JRE 可用的平台编译它。

  1. JVM会检查你定义的类库,看看方法是否存在

  2. 字节码将被解释或 JIT 化,并添加对本机代码的调用。这可能包括 boxing/deboxing 值和将数据转换为合适格式所需的其他内容。这些库有一个特定的接口,它向 Java 编译器解释,它将产生所需的接口逻辑。

  3. 取决于沙箱。默认情况下,本机代码是本机代码。它不调用 Java API,因此 JVM 无法以任何方式管理它。但可能还有其他限制,例如 JVM 可以 运行 带有提供沙盒的库的本机代码,或者操作系统可能有沙盒的方式。

  4. 这取决于你的意思。最后,计算机所做的一切都是机器代码,但在这种情况下并不重要。重要的是翻译和执行部分。那是让一切正常运转的粘合剂。

将系统视为人。 A 只会说日语,但想预订巴黎的酒店。接待员 B 只会说法语。 A 可以得到一个翻译器,将他们的命令翻译成法语,命令接待员 B 并在 return 中将 B 产生的内容翻译成 A 可以理解的形式。这是JNI部分。

  1. 直到你调用方法才知道。本机代码驻留在 .DLL 或 .so 中; java 运行time 查找与您创建的本机方法相对应的特定入口点(如果您使用的是 JNI,则有一个工具可以解析这些方法并创建函数存根,这将导致编译时的那些入口点)。如果想要的入口点不存在,将抛出异常。

  2. JIT 生成的代码并非完全自给自足;它必须不时调用外部本机代码(用于低级 运行 时间例程或 OS 服务)。相同的机制用于为您的本机方法调用代码。

  3. 没有。你可以在那里做你在纯 C/C++ 程序中所做的一切。唯一能阻止它造成任何损害的是你拥有的外部安全措施(登录权限限制、其他 OS 保护、安全软件等),但 VM 不会保护你。

  4. 不,JNI 在 JIT 出现之前就已经存在了。机制是相同的,如果解释器正在 运行 字节码,并且您希望该解释器调用本机代码,您只需要其中的一些逻辑来确定给定的方法是 "external" 和应该被称为本机代码。此信息包含在已编译的 .class 文件中,当解释器或 JIT 加载它时,它会创建一个内存表示,以便于在方法查找时直接调用。