在 TotalCross,有一个问题经常出现——在我们的日常工作中,在演示之后,在 Reddit 讨论中,有时甚至在我们的 Telegram 频道中。让我们一劳永逸地回答它:不,TotalCross 虚拟机不是另一个 Java 虚拟机。本文解释了两者之间的区别、TotalCross 如何与 Java 交互,以及如何知道哪个最适合您的应用程序。
一点历史
TotalCross 虚拟机 (TCVM) 由 Guilherme Hazan 构思,他也是世界上最早的跨平台工具之一 SuperWaba 的创建者,之后他开始致力于 TotalCross。Fabio Sobral 加入团队,开始开发 TotalCross SDK,这是一个雄心勃勃的项目,旨在创建一个跨平台工具,即使在低计算能力的设备(包括当时常见的只有 32MB RAM 和 312MHz CPU 的设备)上也能以最大性能运行应用程序。
TotalCross 的目标
在开发过程中,该项目有三个主要目标
- 优化最常用的 Java 字节码
- 编写基于寄存器的虚拟机
- 构建易于使用的 API 以创建应用程序
优化最常用的 Java 字节码
在攻读硕士学位期间,Guilherme 意识到可以优化一些最常用的字节码,以减少 VM 执行的操作数量。这种优化还可以减小编译包的大小(与 Java 相比最多可减少 39%)。这促成了 TotalCross 字节码的诞生,该字节码仍然在每个使用 TotalCross 制作的应用程序中运行。如果您想了解更多关于 TotalCross 字节码的信息,请查看 Guilherme 的论文。
编写基于寄存器的虚拟机
第二个挑战是用 C 语言编写一个 VM,使其能更好地执行编译后的字节码,尤其是在低计算能力的设备中。
Java 虚拟机 (JVM) 使用基于堆栈的方法,该方法使用堆栈数据结构来存储操作数。操作通过从堆栈中弹出数据、处理数据并将结果推回堆栈来执行。在这种架构中,简单的两个数字相加的操作需要执行四条指令。
相比之下,TCVM 使用基于寄存器的架构,该架构类似于大多数硬件使用的架构,其中操作数存储在基于 CPU 寄存器的结构中。这种架构比基于堆栈的架构更难编码,因为指令必须直接寻址操作数,而不是仅仅从堆栈中弹出最后一个操作数。但是,两个数字相加操作只需一条指令即可完成(与四条指令相比),从而节省了 CPU 周期并减少了字节码的大小。

(Bruno Muniz,CC BY-SA 4.0)
JVM 和 TCVM 之间的另外两个区别是数组和本机接口。
数组
Java 中的数组不一定映射到单个连续的内存空间。要使用需要连续内存空间的本机函数(例如读取或写入 I/O 函数),程序员必须为 Java 堆分配第二个内存空间,锁定数组对象,然后将数据复制到/从两个结构中复制数据。对于 I/O 操作来说,这是很多步骤和内存操作。
为了解决这个问题,Java 添加了 ByteBuffer,它可以封装 Java 数组或指向堆外内存的直接指针。此解决方案大大减少了使用原始数组的开销,但仍然需要大量的结构管理,并且通常是经验不足的开发人员的麻烦来源。添加此结构对以前依赖原始数组的实现没有影响。
在 TotalCross 中,数组始终是连续的,这意味着本机代码可以直接访问和使用 Java 原始数组,而无需额外的步骤。
TotalCross 本机接口
在 TotalCross 中,本机代码使用 TotalCross 本机接口 (TCNI) 编写,该接口将 Java 方法绑定到本机代码中实现的函数。乍一看,它可能类似于 Java 的 JNI,但它的实现方式却大相径庭。
在 Java 中,处理对象可能很麻烦。程序员必须处理大量锁,以确保对象在以本机方式访问时不会被移动或损坏。
在 TotalCross 中,大多数情况下可以直接使用内存地址访问对象,无需锁或内存搜索。
构建易于使用的 API 以创建应用程序
最后,为了让开发人员构建出色的应用程序,Fabio 和 Guilherme 需要提供组件 API,而显而易见的选择是使用 Java。Java 仍然是开发人员中最流行的编程语言之一,即使 TCVM 不是 JVM,使用 Java 仍然带来了许多好处,例如 JVM 上运行的应用程序之间的代码互操作性、大量可用的设计模式等等。
他们决定使用 Java SDK 的子集,并从头开始编写所有 UI 组件,因为 Swing 和 AWT 没有被广泛采用。因此,TotalCross API 诞生了。
TotalCross 在实践中如何工作

(Bruno Muniz,CC BY-SA 4.0)
为了解释 Java、Java 开发工具包 (JDK) 和 JVM 与 TotalCross 的关系,我将该过程分为两个主要阶段:开发和运行时。
在开发阶段,开发人员需要 TotalCross API (tc.jar
) 和 JDK 来编写应用程序的源代码并将代码编译为 Java 字节码。可以使用 JVM 支持的任何其他语言,因为它们已经转换为 Java 字节码。
为了从 Java 字节码转换为 TotalCross 字节码,开发人员必须使用一个名为 tc.Deploy
(TC 转换器)的类,该类将一种字节码转换为另一种字节码(有多种使用此类的方法,包括一个 Visual Studio Code 插件,其中包含 Maven 集成)。
这个 tc.Deploy
类还将打包在目标设备上运行应用程序所需的一切,包括 TCVM、应用程序字节码、TotalCross API 字节码、资源(图像、字体等)、SQLite 等。
运行时阶段使应用程序能够在多个操作系统上运行,包括 Android、iOS、Linux、Windows、Windows CE 和 Linux ARM。在此阶段,对 Java、JVM 或 JDK 没有要求。应用程序完全在 TCVM 中运行。TCVM 嵌入在应用程序中,最终用户永远不会知道 VM 是否正在运行,从而使该过程完全无缝。
您应该使用哪个?
TotalCross VM 和 Java VM 采用不同的方法。不仅它们的字节码和架构不同;它们的用途也不同。虽然 TotalCross 适用于边缘应用程序,但 Java 最好的用途是在 Web 应用程序、服务器端应用程序和中间件等领域。
从技术和战略角度来看,与 JVM 脱钩对 TotalCross 非常有用,因为它大大改善了应用程序的占用空间(TCVM 使用 4MB,并且正在进行大量研究以进一步减少它)。由于没有与 JVM 保持兼容性的束缚,并且 TotalCross 的架构是基于寄存器的,因此 TotalCross VM 为在低计算能力和高端设备上运行应用程序提供了本机性能,同时保留了 Java 或 Kotlin 等高级编程语言的优势。
要决定是使用“纯”Java 应用程序还是 TotalCross 应用程序,请查看您的应用程序和目标设备的要求。这两种技术采用不同的方法,旨在解决不同的问题,但最终,您是赢家,因为您可以利用您的代码并在它们之间共享代码。
您应该查看 GitHub 上的源代码 并得出您自己的结论。并让我们知道您的想法 =)
1 条评论