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

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

本文关键字:技术上 互操作 本机代码 托管代码 混合 代码      更新时间:2023-10-16

我基本上理解托管代码和本机代码的概念及其区别。但是,从技术上讲,他们如何能够相互交流呢?想象一下下面的例子:

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

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

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

  3. 这一切如何符合沙箱模式?本机代码是否也在沙盒中执行?

  4. 它工作是因为(java和c++)代码最终都是机器代码吗?

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

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

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

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

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

  4. 不,JNI甚至在JIT出现之前就已经存在了。机制是一样的,如果字节码由解释器运行,并且您希望该解释器调用本机代码,则只需要其中的一些逻辑来确定给定的方法是"外部"的,并且应该作为本机代码调用。这些信息包含在已编译的.class文件中,当解释器或JIT加载它时,它会创建一个内存表示,从而可以方便地在方法查找时直接调用。

  1. JVM将检查您定义的库,并查看方法是否存在

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

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

  4. 这取决于你的意思。最终,计算机所做的一切都是机器代码,但在这种情况下并不重要。重要的是翻译和执行部分。这是使一切正常工作的胶水。

把系统想象成人。人A只会说日语,但想在巴黎预订一家酒店。接待员B只会说法语。人员A可以请一名翻译将他们的命令翻译成法语,命令接待员B将B制作的内容翻译成人员A理解的形式。这是JNI的部分。

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

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

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