字节码执行学习总结:影响字节码执行性能因素
字节码执行总结:影响字节码执行性能因素
- 前言
- 相同的 Java 程序在不同 JVM、不同 CPU 架构上可能具有不同的执行路径和性能的原因分析
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
相同的 Java 程序在不同 JVM、不同 CPU 架构上可能具有不同的执行路径和性能的原因分析
学习方向:JVMS、HotSpot、JIT 编译、CPU 指令集与系统性能分析
一、核心认识:Java 跨平台不等于执行过程完全一致
从系统工程师角度理解 Java 跨平台,首先需要明确一个重要概念:
Java 跨平台保证的是程序具有相对统一的运行规范,而不是保证不同 JVM、不同操作系统和不同 CPU 架构采用完全相同的执行路径,更不保证具有相同的执行性能。
同一份 Java 源代码经过 javac 编译后,可以生成相同或等价的 Class 文件。但是,当 Class 文件进入不同 JVM 后,其实际执行过程可能发生变化。
例如:
- 不同 JVM 可以采用不同的解释器实现。
- 不同 JVM 可以采用不同的 JIT 编译策略。
- 不同 JVM 可以采用不同的垃圾收集器。
- 不同 CPU 架构具有不同的指令集和内存排序约束。
- 不同操作系统具有不同的线程调度、内存管理和系统调用实现。
- JIT 编译器会根据运行时数据、CPU 特性和代码执行情况生成不同的机器码。
因此,需要区分三个层次:
- 规范层: JVMS 和 Java API 定义程序应当遵守的语义。
- 虚拟机实现层: JVM 决定如何加载、解释、编译和管理 Java 程序。
- 硬件执行层: CPU 根据目标指令集执行本地机器码。
从系统工程角度,研究性能差异,实际上就是研究这三个层次之间的实现差异。
二、Java 程序在不同 JVM 和 CPU 上的执行过程

整体执行链路可以理解为:
Java 源代码
|
v
javac 编译
|
v
Class 文件
字节码
|
+-------+-------+
| |
v v
JVM A JVM B
| |
类加载 类加载
| |
字节码解释 字节码解释
| |
JIT 编译 JIT 编译
| |
优化策略 A 优化策略 B
| |
v v
x86-64 机器码 AArch64 机器码
| |
v v
x86 CPU ARM CPU
需要注意:图中是概念性执行链路。实际 JVM 可以同时使用解释执行和编译执行,也可能通过分层编译、OSR(On-Stack Replacement)等机制改变正在运行的方法的执行方式。
学习重点:Class 文件相同,不代表最终生成的机器码相同;机器码不同,也不代表 Java 程序的外部可观察语义可以随意改变。
三、不同 JVM 为什么会产生不同的执行路径
JVM 规范定义了虚拟机应当遵守的行为,但没有要求所有 JVM 必须采用同一种内部实现。
JVMS 的核心思想是:
实现可以不同,但必须保持规定的字节码语义。
例如,同一个 Class 文件,可以由 HotSpot、OpenJ9 或 GraalVM 运行。它们可能采用不同的解释器、编译器、内存管理机制和运行时优化策略。只要符合相应的 Java 平台规范,就不要求其内部执行过程完全一致。
3.1 JVM 执行引擎不同
以 OpenJDK HotSpot 为例,其执行引擎主要包含:
| 执行组件 | 主要职责 | 性能特点 |
|---|---|---|
| Interpreter | 解释执行字节码 | 启动成本低,执行效率通常较低 |
| C1 Compiler | 快速 JIT 编译 | 编译速度快,适合快速优化 |
| C2 Compiler | 高度优化 JIT 编译 | 编译成本较高,适合热点代码 |
| Code Cache | 保存编译后的机器码 | 支持后续直接执行编译代码 |
HotSpot 可以通过分层编译,在不同执行阶段选择不同的执行方式。
典型的分层编译等级如下:
| 编译等级 | 执行方式 | 作用 |
|---|---|---|
| Level 0 | Interpreter | 解释执行并收集运行信息 |
| Level 1 | C1 | 快速编译,不进行完整性能分析 |
| Level 2 | C1 | 收集部分运行信息 |
| Level 3 | C1 | 收集较完整的运行信息 |
| Level 4 | C2 | 基于运行信息进行深度优化 |
其基本执行过程:
Java 方法首次执行
|
v
Interpreter
|
收集运行信息
|
v
C1 编译
|
继续收集信息
|
v
C2 编译
|
v
优化机器码
|
v
CPU 执行
注意:这不是每个方法都必须经过的固定流程。方法可能停留在解释执行或 C1,也可能根据运行状态直接进入其他编译等级。
HotSpot 还会根据编译队列长度、方法调用次数、循环回边计数器等因素动态调整编译决策。
3.2 不同 JVM 的优化策略不同
即使运行相同的 Java 代码,不同 JVM 也可能采用不同的优化策略。
例如:
(1)方法内联(Inlining)
假设存在以下代码:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int calculate(int x) {
// JIT 有可能将 add() 方法内联到这里
return add(x, 10);
}
}
解释执行时,可能需要通过方法调用机制执行 add()。
JIT 编译后,可能将其优化为类似:
public int calculate(int x) {
return x + 10;
}
这属于概念性优化结果,不代表所有 JVM 都一定生成完全相同的机器码。
不同 JVM 的内联阈值、调用点分析、编译成本模型可能不同,因此可能产生不同的优化结果。
(2)逃逸分析(Escape Analysis)
public class PointTest {
public int calculate() {
// Point 对象只在当前方法中使用
Point point = new Point(10, 20);
return point.x + point.y;
}
static class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
}
如果 JIT 能够证明对象不会逃逸到当前方法之外,就可能通过标量替换等优化消除对象分配。
需要注意:
- 标量替换不等于将 Java 对象真正分配到栈上。
- 逃逸分析不一定能够消除所有对象分配。
- 不同 JVM 的逃逸分析能力和优化条件可能不同。
(3)循环优化
public class LoopTest {
public long sum(int[] array) {
long result = 0;
for (int i = 0; i < array.length; i++) {
result += array[i];
}
return result;
}
}
不同 JIT 编译器可能采用:
- 循环展开(Loop Unrolling)。
- 循环不变量外提(Loop Invariant Code Motion)。
- 范围检查消除(Range Check Elimination)。
- 自动向量化(Auto Vectorization)。
这些优化会改变最终机器指令的数量和执行顺序,但必须保证 Java 语义要求的结果。
3.3 不同 JVM 的垃圾收集器也会影响性能
垃圾收集器虽然不直接决定每条 Java 字节码如何执行,但会影响整个程序的运行表现。
例如:
| GC 类型 | 主要特点 | 关注指标 |
|---|---|---|
| Serial GC | 单线程收集 | 小型堆、简单场景 |
| Parallel GC | 关注吞吐量 | 总体处理能力 |
| G1 GC | 分区管理,兼顾吞吐量和停顿 | 延迟与吞吐量平衡 |
| ZGC | 面向低停顿目标 | 尾延迟、停顿时间 |
同一个 Java 程序,在不同 GC 配置下可能出现不同的:
- Young GC 频率。
- Full GC 频率。
- 对象分配成本。
- GC CPU 占用。
- STW 停顿时间。
- 内存占用情况。
例如,两个 JVM 的业务代码执行时间可能非常接近,但由于 GC 策略不同,最终请求的 P99 延迟可能出现明显差异。
系统工程师需要注意:应用性能不仅取决于 JIT 生成的机器码,也取决于 JVM 的内存管理和垃圾收集行为。
四、不同 CPU 架构为什么会产生不同的机器码
这是理解 Java 跨平台性能差异的核心。
JVM 最终需要把字节码转换为目标 CPU 能够执行的本地机器指令。
不同 CPU 架构具有不同的指令集、寄存器组织、内存模型和硬件执行机制,因此同一段 Java 字节码可能生成完全不同的机器码。
4.1 x86-64 与 AArch64 指令集不同
X86-64

AArch64

两种架构的主要差异:
| 对比维度 | x86-64 | AArch64 |
|---|---|---|
| 指令编码 | 变长指令 | 固定长度的基础指令编码 |
| 通用寄存器 | 16 个主要 64 位通用寄存器 | 31 个 64 位通用寄存器(X0~X30) |
| 内存访问 | 支持多种内存操作数形式 | 典型采用 Load/Store 模型 |
| SIMD | SSE、AVX、AVX2、AVX-512 等 | NEON、SVE、SVE2 等 |
| 原子操作 | LOCK 前缀等机制 | LSE 等原子指令扩展 |
| 内存排序 | 相对较强 | 相对较弱,需要更明确的排序指令 |
例如,Java 代码:
public int add(int a, int b) {
return a + b;
}
在 x86-64 上,JIT 可能生成类似:
; x86-64 示例
mov eax, edi
add eax, esi
ret
在 AArch64 上,可能生成类似:
// AArch64 示例
add w0, w0, w1
ret
这里展示的是简化的示意指令,实际寄存器分配、参数传递和指令选择由 JVM 编译器与平台 ABI 决定。
可以发现:
- Java 源代码相同。
- Java 字节码语义相同。
- 目标 CPU 指令集不同。
- JIT 生成的机器指令不同。
因此,最终执行路径和硬件执行成本也可能不同。
4.2 CPU 指令集扩展影响性能
现代 CPU 通常具有多种指令集扩展。
例如:
| 指令集 | 主要用途 |
|---|---|
| SSE | SIMD 数据并行计算 |
| AVX / AVX2 | 更宽的向量计算 |
| AVX-512 | 更宽的 SIMD 运算 |
| NEON | ARM SIMD 运算 |
| SVE / SVE2 | ARM 可扩展向量计算 |
假设 Java 程序执行大量数组计算:
public void addArrays(
float[] a,
float[] b,
float[] result) {
for (int i = 0; i < a.length; i++) {
result[i] = a[i] + b[i];
}
}
普通执行方式可能逐个处理数组元素。
如果 JVM 能够进行有效的循环向量化,就可能一次处理多个元素。
例如:
普通执行:
a[0] + b[0]
a[1] + b[1]
a[2] + b[2]
a[3] + b[3]
向量化执行:
[ a0, a1, a2, a3 ]
+
[ b0, b1, b2, b3 ]
|
v
[ r0, r1, r2, r3 ]
但需要注意,向量化并不是一定发生的。
它取决于:
- JIT 编译器是否支持相应优化。
- CPU 是否支持对应的向量指令。
- 循环结构是否满足向量化条件。
- 数据对齐、别名分析和边界处理是否允许优化。
- 向量化成本是否低于普通标量执行成本。
因此,即使运行相同的 Java 数组计算程序,在不同 CPU 上也可能表现出不同的吞吐量。
4.3 CPU 内存模型不同,影响并发执行
这一部分与之前学习的 JMM、Happens-Before、Volatile、OrderAccess 有直接关系。
Java 内存模型定义的是 Java 程序的内存语义,而 CPU 内存模型定义的是硬件层面的内存访问和排序约束。
两者不是同一个概念。
以 x86-64 和 AArch64 为例:
- x86-64 通常采用较强的 TSO 内存排序模型。
- AArch64 允许更多种类的内存访问重排序。
- AArch64 在需要特定顺序保证时,可能使用
DMB、LDAR、STLR等指令。 - x86-64 则可能根据操作类型使用普通 Load/Store、
MFENCE、带锁原子指令等机制。
例如:
public class VisibilityTest {
private int data = 0;
private volatile boolean ready = false;
// 线程 A
public void producer() {
data = 100;
// volatile 写:发布之前的数据
ready = true;
}
// 线程 B
public void consumer() {
// volatile 读:获取发布状态
if (ready) {
System.out.println(data);
}
}
}
这里的关键是:
volatile 写与后续观察到该写入的 volatile 读之间建立同步关系,保证 data = 100 对消费者线程可见。
从底层实现来看:
Java volatile 写
|
v
JIT 内存排序约束
|
v
目标架构对应的屏障或有序访存指令
|
v
CPU 执行
在 x86-64 和 AArch64 上,底层实现可能不同,但必须满足 Java Memory Model 的要求。
重要结论:不能因为 x86-64 的内存排序相对较强,就认为 Java 程序可以不使用 volatile、锁或其他正确同步机制。
硬件实现不能替代 Java 程序的同步语义。
五、结合 OpenJDK HotSpot 源码分析执行路径差异
从系统工程师角度,不能只停留在理论层面,还需要知道 JVM 源码中哪些位置决定了执行路径。
以下以 OpenJDK HotSpot 为例。
5.1 分层编译策略源码
源码位置:
src/hotspot/share/compiler/compilationPolicy.hpp
可以重点关注编译策略及编译等级的定义。
OpenJDK 中的编译策略会根据方法运行时收集的数据决定后续编译行为。
核心逻辑可以概括为:
/*
* HotSpot 分层编译的典型执行等级:
*
* Level 0:解释器
* Level 1:C1,不进行完整 Profiling
* Level 2:C1,收集部分运行数据
* Level 3:C1,收集完整 Profiling 数据
* Level 4:C2,执行深度优化
*
* 注意:
* 具体等级和策略以对应 JDK 版本的源码为准。
*/
void compilation_policy_example() {
// 1. 获取方法执行次数、循环回边次数等运行信息
// 2. 根据方法热度判断是否需要编译
// 3. 考虑编译线程队列及系统负载
// 4. 决定是否提交 C1 或 C2 编译任务
// 5. 编译完成后,将执行入口切换到编译后的代码
}
这里的代码是用于说明决策过程的伪代码,不是 OpenJDK 的原始实现。
建议重点阅读以下文件:
| 源码文件 | 学习重点 |
|---|---|
src/hotspot/share/compiler/compilationPolicy.hpp | 编译策略定义 |
src/hotspot/share/compiler/compilationPolicy.cpp | 编译决策实现 |
src/hotspot/share/compiler/compileBroker.cpp | 编译任务提交和管理 |
src/hotspot/share/opto/compile.cpp | C2 编译过程 |
src/hotspot/share/c1/c1_Compiler.cpp | C1 编译入口 |
其中,CompileBroker 值得重点研究,因为它连接了运行时编译请求与实际编译线程。
可以通过以下命令观察方法的编译情况:
java \
-XX:+UnlockDiagnosticVMOptions \
-XX:+PrintCompilation \
-XX:+PrintInlining \
MyApplication
通过观察输出,可以分析:
- 哪些方法被编译。
- 方法进入了哪个编译等级。
- 是否发生方法内联。
- 是否存在重新编译。
- 是否出现反优化。
5.2 CPU 架构相关源码
HotSpot 的机器码生成与 CPU 架构密切相关。
建议重点研究:
| 源码目录 | 主要职责 |
|---|---|
src/hotspot/cpu/x86/ | x86-64 平台相关实现 |
src/hotspot/cpu/aarch64/ | AArch64 平台相关实现 |
src/hotspot/share/opto/ | C2 编译器中间表示与优化 |
src/hotspot/share/runtime/ | JVM 运行时机制 |
例如,C2 编译器并不是简单地把 Java 字节码逐条翻译成机器指令,而是经过中间表示、控制流分析、数据流分析和指令选择等阶段。
可以把过程理解为:
Java 字节码
|
v
字节码解析
|
v
C2 中间表示(IR)
|
v
类型分析与数据流分析
|
v
优化与指令选择
|
v
寄存器分配
|
v
目标 CPU 汇编指令
|
v
本地机器码
其中:
opto/重点关注平台无关的编译优化。cpu/x86/重点关注 x86 平台的指令生成。cpu/aarch64/重点关注 ARM 64 位平台的指令生成。
学习总结:JIT 编译器的优化思想可以具有平台通用性,但最终机器指令必须适配目标 CPU。
5.3 OrderAccess 与 CPU 内存屏障源码
结合之前学习的 JMM、OrderAccess 和 CPU 内存排序,可以进一步理解不同架构如何实现相同的 Java 内存语义。
建议关注:
src/hotspot/share/runtime/orderAccess.hpp
src/hotspot/cpu/x86/orderAccess_x86.hpp
src/hotspot/cpu/aarch64/orderAccess_aarch64.hpp
这些文件体现了 HotSpot 如何抽象和实现不同平台的内存排序操作。
示意代码:
// 以下为概念性伪代码,不是 OpenJDK 原始源码。
void publish_data() {
data = 100;
// 保证之前的数据写入不会被重排到发布操作之后
OrderAccess::release();
ready = true;
}
void consume_data() {
if (ready) {
// 获取发布状态,建立必要的内存排序约束
OrderAccess::acquire();
// 读取已经发布的数据
use(data);
}
}
实际实现中,屏障可能被编译器屏障、CPU 屏障或带有内存语义的原子指令实现,也可能根据平台特性省略不必要的硬件指令。
特别需要注意:上述伪代码用于说明 Acquire/Release 思想,不能直接视为 Java volatile 的完整实现。
建议从以下三个层次进行分析:
- Java 层:
volatile、synchronized、VarHandle。 - JVM 层:编译器内存屏障、OrderAccess、原子操作。
- CPU 层:x86-64 与 AArch64 的实际内存排序指令。
这三个层次必须结合起来研究,才能真正理解 Java 并发程序在不同 CPU 架构上的执行差异。
六、除了 JVM 和 CPU,还有哪些因素影响执行性能
实际生产环境中,性能差异往往不是单一因素造成的。
6.1 操作系统线程调度
Java 线程最终需要映射到操作系统线程执行。
不同操作系统的线程调度策略、上下文切换成本、线程亲和性和调度优先级可能不同。
例如:
Java Thread
|
v
JVM 线程实现
|
v
操作系统线程
|
v
OS 调度器
|
v
CPU Core
当系统负载较高时,可能出现:
- 线程上下文切换增加。
- CPU 运行队列变长。
- 线程频繁迁移到不同 CPU 核心。
- CPU Cache 命中率下降。
- 锁竞争和线程等待时间增加。
这些因素都会影响 Java 程序的实际性能。
6.2 CPU Cache 和内存访问延迟
CPU 并不是每次访问变量都直接访问主内存。
现代处理器通常具有多级缓存:
CPU Core
|
+-----+-----+
| |
L1 L1
| |
+-----+-----+
|
L2
|
L3
|
主内存
不同 CPU 的缓存容量、缓存一致性协议、缓存行大小、内存带宽以及 NUMA 架构可能不同。
例如,Java 程序中存在大量对象访问时:
- 对象布局可能影响缓存局部性。
- 对象分配方式可能影响内存访问。
- 指针追踪可能导致 Cache Miss。
- 多线程共享数据可能引发 Cache Line 竞争。
- NUMA 跨节点访问可能增加内存访问延迟。
因此,不能只通过比较机器的 CPU 主频来判断 Java 程序的执行性能。
6.3 JVM 参数和运行配置
即使是同一款 JVM、同一颗 CPU,如果启动参数不同,也可能产生明显的性能差异。
例如:
| JVM 参数 | 影响 |
|---|---|
-Xms | 初始堆大小 |
-Xmx | 最大堆大小 |
-XX:+UseG1GC | 选择 G1 垃圾收集器 |
-XX:+UseZGC | 选择 ZGC |
-XX:-TieredCompilation | 关闭分层编译 |
-Xint | 仅使用解释器 |
-XX:ActiveProcessorCount | 指定 JVM 感知的可用处理器数量 |
这些配置会影响:
- JIT 编译行为。
- GC 执行频率。
- 内存占用。
- 线程数量。
- 启动时间。
- 吞吐量与延迟。
因此,性能测试时必须确保 JVM 配置具有可比性。
七、系统工程师如何验证不同 JVM、不同 CPU 的性能差异
理解原理之后,需要通过实验验证。
建议构建一个统一的 Java Benchmark,在不同环境下进行测试。
7.1 测试环境设计
| 测试环境 | JVM | CPU |
|---|---|---|
| 环境 A | OpenJDK HotSpot | x86-64 |
| 环境 B | OpenJDK HotSpot | AArch64 |
| 环境 C | OpenJ9 | x86-64 |
| 环境 D | GraalVM | x86-64 或 AArch64 |
尽可能保持 Java 版本、业务代码、数据集和 JVM 参数一致。
同时记录操作系统、CPU 型号、核心数量、内存容量和 GC 配置。
7.2 编写统一的测试程序
public class CpuBenchmark {
private static final int SIZE = 10_000_000;
public static long calculate() {
long result = 0;
for (int i = 0; i < SIZE; i++) {
// 测试整数运算与循环执行性能
result += (long) i * 3;
}
return result;
}
public static void main(String[] args) {
// 预热:让 JVM 收集运行信息并触发可能的 JIT 编译
for (int i = 0; i < 20; i++) {
calculate();
}
long start = System.nanoTime();
long result = calculate();
long end = System.nanoTime();
System.out.println("result = " + result);
System.out.println(
"耗时(ns) = " + (end - start)
);
}
}
这只是用于理解执行路径的简单示例,并不是严谨的性能测试程序。正式测试建议使用 JMH,避免死代码消除、预热不足、测试次数不合理等问题。
7.3 建议重点采集的性能指标
| 指标 | 关注内容 |
|---|---|
| Throughput | 单位时间处理能力 |
| Average Latency | 平均响应时间 |
| P95 / P99 | 尾延迟 |
| CPU Usage | CPU 使用率 |
| IPC | 每周期执行指令数 |
| Cache Miss | 缓存未命中情况 |
| Context Switch | 上下文切换次数 |
| GC Pause | 垃圾收集停顿 |
| Allocation Rate | 对象分配速率 |
| Compilation Time | JIT 编译时间 |
7.4 推荐使用的诊断工具
JVM 编译诊断
-
-XX:+PrintCompilation -
-XX:+PrintInlining -
JFR(Java Flight Recorder)
操作系统性能分析
-
Linux
perf -
perf stat -
perf record -
perf reportJava 微基准测试
-
JMH(Java Microbenchmark Harness)
-
async-profiler
-
JITWatch
7.5 性能测试时必须注意的几个问题
- 预热时间必须充分,不能把解释执行阶段和稳定运行阶段混为一谈。
- 不要只测试一次,应当进行多轮测试并分析统计分布。
- 应当区分冷启动性能和稳定运行性能。
- 必须关注 GC、JIT 编译和操作系统调度造成的额外开销。
- 比较不同 CPU 时,应尽量控制 CPU 频率、功耗模式和后台负载。
- 对于并发程序,还需要考虑线程数、CPU 核数和 NUMA 影响。
特别提醒:不能因为某个平台一次运行更快,就直接认定该 JVM 或 CPU 架构更优秀。必须通过重复实验、性能剖析和硬件计数器找到真正的性能瓶颈。
八、总结:相同 Java 程序为什么会有不同执行路径和性能
从系统工程师角度,可以将原因归纳为五个方面。
影响 Java 执行路径和性能的五个核心因素

8.1 必须掌握的六个知识点
| 知识点 | 需要理解的问题 |
|---|---|
| JVMS | 为什么不同 JVM 可以采用不同实现? |
| HotSpot | 解释器、C1、C2 如何决定执行路径? |
| JIT | 为什么相同字节码会生成不同机器码? |
| CPU ISA | 为什么 x86-64 和 AArch64 指令不同? |
| JMM | 不同 CPU 如何保证 Java 内存语义? |
| OS | 为什么线程调度和内存管理会影响性能? |
九、最终学习心得
作为系统工程师,我认为学习 Java 跨平台执行差异,不能仅仅停留在 Java 语言和 JVM 参数层面,而应该建立完整的软硬件协同分析思维。
首先,JVMS 规定了 Java 程序的执行语义,但没有限制 JVM 必须采用统一的内部实现。其次,JIT 编译器会根据运行时 Profiling 数据、编译策略以及目标 CPU 的硬件能力,对字节码进行不同程度的优化。最后,CPU 根据自身指令集、缓存结构和内存排序约束执行生成的本地机器码。
因此,相同 Java 程序在不同 JVM、不同 CPU 架构上可能具有不同的执行路径和性能表现,这是 Java 虚拟机实现独立性与硬件架构差异共同作用的结果。
最重要的学习结论:
- Java 字节码提供跨平台的执行语义。
- JVM 决定字节码采用什么方式执行。
- JIT 决定如何将热点代码优化为机器码。
- CPU 决定如何执行目标机器指令。
- 操作系统和硬件资源共同影响最终运行性能。
对于系统工程师而言,真正需要掌握的是从 Java 字节码到 JVM 执行引擎,再到 CPU 本地机器码的完整执行链路,并且能够通过 OpenJDK 源码、JIT 编译日志、汇编代码和硬件性能计数器定位不同平台之间的性能差异。
这也是从 Java 应用开发视角转向 JVM 与操作系统底层系统工程视角的重要一步。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)