并发王座易主?Java 21 虚拟线程强势崛起,Go & Kotlin还稳得住吗 | 盘点
采访嘉宾 | 李三红
过去一年,编程语言发生了不少新变化。
据 JetBrain 前不久发布的 《2023 开发者生态系统现状》调研报告,在开发者主要采用的编程语言中,最受欢迎的分别是 Java、Python、JavaScript,Java 在 2023 年重夺第一名宝座,JavaScript 则在下降三个百分点后跌至第三;Rust 在 2023 年最受欢迎的编程语言中,创造了新的使用记录,其用户群在过去五年中稳步增长,有望凭借其严格的安全性和内存所有权机制取代 C++;此外,Rust 2023 年首次取代 Go 成为希望迁移到其他语言的开发者的首选,而且 Go 用户也是第一批准备采用 Rust 的人,JetBrains 数据表明,有六分之一的 Go 用户正在考虑采用 Rust。
伴随着火热发展的大模型技术浪潮,也有一些编程语言新玩家涌现出来。比如由 Swift 之父 Chris Lattner 带领团队推出的 Mojo,其目标是统一碎片化的 AI 技术栈;又比如由 IDEA 研究院基础软件中心负责人张宏波及其团队打造的 Moonbit,推出之初其定位为专为云计算和边缘计算设计的 WebAssembly 语言,但如今 Moonbit 的最新定位已经演进为云和大模型时代下的开发者平台。
那么,大模型时代我们应该关注编程语言的哪些变化?本次“InfoQ 年度技术盘点与展望”专题中,InfoQ 邀请了 Java、MoonBit、Rust、WebAssembly 等不同编程语言的代表性技术专家、团队分享他们的观察和思考。本文是 “2023 InfoQ 年度技术盘点与展望” 系列文章之一,由 InfoQ 编辑部制作呈现,我们采访了阿里云程序语言与编译器团队负责人、Java Champion 李三红老师,他也是国内 Java 编程语言最具代表性的技术专家之一。他带我们一同回顾了过去一年编程语言整体和 Java 本身的重要进展。在他看来,Rust 确实在系统软件有巨大的影响力,但在业务领域 Java 和 Go 仍会占据主导地位,因为业务快速迭代需要技术本身的平民化;而 2023 年随着 Java 21 版本发布的虚拟线程特性,有助于在并发方面巩固 Java 在业务处理领域的地位。他还提及,大模型和生成式 AI 的发展对 AI 算力的提升提出了很高的要求,编程语言或编程系统承载着释放底层并行硬件算力的使命。
以下为访谈实录,经过不改变原意的编辑:
李三红: 我首先介绍一下我所负责部门的基本情况。我们属于阿里云的基础软件部门,基本上都是在编写系统软件,不管是编译器还是操作系统,还有一些云原生组件,其实都属于系统软件领域,所以我主要从系统软件这个角度展开讨论。
就编程语言领域来讲,我的感受也是一样的,就是 Rust 确实比较火,而且随之而来的是大家对内存安全(memory safety)问题的重视。Rust 的设计原则是优先考虑内存安全。使用 C、C++ 这样的编程语言,我们很容易会遇到因为不正确的内存访问导致的 Security Vulnerability 问题(据 2020 年早些时候的一篇报告,Google Chromium 团队发现 C++ 编写的 Chrome 代码库中 70% 的安全漏洞与内存管理和安全相关 [1])。Rust 作为系统编程语言,解决了内存安全的问题,同时兼具了像 C 和 C++ 这样的良好性能。
和 Java 相比的话,Java 语言在设计之初,也充分考虑了内存安全的问题(比如 ArrayIndexOutOfBoundsException 运行时检查),Java 也被称为 Memory-safe 的语言。但是,目前使用 Java 语言编写系统软件还是不太可行,主要还是性能问题。而 Rust 在编写系统软件方面,则具有非常独特的优势,当然它的学习曲线可能高一些。在最近召开的日本开源峰会(Open Source Summit Japan),邀请到了 Linux 的作者 Linus Torvalds,他表示今年 Linux 一些重要的子系统(major subsystems)可能会使用 Rust 重写。所以,我认为在整个系统软件领域 ,Rust 的确是讨论比较多,影响也比较大的一门编程语言 。
李三红: 我觉得在业务领域,Java 和 Go 还是会占据主导地位。原因在于 Rust 的学习成本的确比较高。如果语言本身的学习成本比较高,而业务又要快速发展的话,往往会导致一些问题,比如,公司的人员储备以及对技术的学习理解和掌握都会出现一些不匹配或者产生较大的矛盾。业务本身的迭代会非常快,比如在阿里,一个 Java 应用每一星期可能会有三到四个版本的发布。这样的快速业务迭代就需要技术本身的平民化。 就像 James Gosling 在 1997 年发表的论文《The Feel of Java》所言,Java 是一门蓝领语言。它非常平民化,适合快速发展的业务,每门语言都有自己的定位。
InfoQ:对的,在业务领域,对生产率要求比较高,相对来讲对代码的性能不像系统软件那么高,另外再考虑到人才储备的因素,我们应该还是优先选择一些工业级的语言,比如 Java、Go、Node.js 等比较流行的语言。总而言之,我们需要根据业务场景和技术需求,选择合适的解决方案。
李三红:正如你所言,Java 现在每年有两个版本,发布速度是很快的,这确实推进了 Java 的创新速度,让我们感觉 Java 添加新特性更快、更有活力了。2023 年 Java 发布了两个版本,分别是 Java 20 和 Java 21,其中 Java 21 是两年一次的 LTS 版本,也就是 Long Term Support 版本。我个人认为,Java 21 是一个非常重要的发布,一方面因为它是 LTS 版本,另一方面是因为在 Java 21 中包含了虚拟线程(Virtual Threads)特性。我认为在整个 Java 演进上这都是一个非常重要的特性。
其实 Java 1.0 版本就已经将线程作为一个 Built-in 特性来设计了,它就是 Java 语言的一部分。而在 Java 之前的 C++,设计之初线程并不是 C++ 标准的一部分。直到 C++ 11,标准库才扩展支持线程库能力。Java 在设计之初就把线程设计为 Java 语言的一部分,Java 的开发者很容易编写并发的多线程程序,开发和认知的代价都非常小。Go 语言在 2009 年诞生, 将并发(Concurrency)作为 Go 语言的一等公民(First-Class Citizen),通过轻量级的“Goroutines”为并发执行提供支持。在 Go 语言中使用 Goroutine 是非常自然和容易的。Kotlin(JVM 生态语言)诞生于 2011 年,Kotlin 也是在设计之初就在语言层面支持了协程。
2005 年,C++ 专家 Herb Sutter 在 Dr. Dobb's Journal(DDJ)发表了著名的文章《The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software》, 谈到随着摩尔定律的终结,计算机软件将不得不、或者说被迫处理好基于多核处理器的大规模并发程序的效率问题,这对软件的并发性能提出了极致的要求,这也是 Go、Kotlin 等语言把 Coroutine 纳入到语言标准支持的原动力。
其实也是由于历史的原因(在 Java 8 之前 Java 的创新速度非常慢),Java 社区直到大约 2016 年左右,开始重视轻量级线程。Java 学习了很多前人的经验,包括编程语言之间的互相借鉴和学习,在 Java 19 中首次引入虚拟线程,经过两个版本的迭代,虚拟线程最终在 Java 21 成为了一个标准的特性。虽然还存在一些局限,在生产环境有一些限制,但是这并不妨碍它未来的发展。虚拟线程特性有助于在并发方面巩固 Java 在业务处理领域的地位。
李三红: 阿里有自己的基于 OpenJDK 的发行版,也就是 Alibaba Dragonwell。就协程来讲,Alibaba Dragonwell 扩展版(Extended Edition)有一个自己的协程实现叫 Wisp,它 2015 年左右在阿里内部孵化,2017 年就已经在阿里大规模使用了。Wisp 解决了使用 synchronized block 导致协程无法切换的问题。阿里内部相对来讲是一个封闭的 Java 生态系统,我们可以使用我们自己的 Wisp 协程解决线上的高并发性能问题。
就目前看,在生产环境,现在还是不太可能去使用虚拟线程。由对象监视器锁所导致的虚拟线程 pinning 的问题,如果要去做对应的代码修改,工作量是很大的,这也是我觉得在生产环境大规模使用虚拟线程的一个阻碍因素。当然,现在整个社区也在考虑如何去解决这个问题。
InfoQ:对,我们也看到一些开源框架,比如 Spring、Quarkus,都在虚拟线程方面提供了很多的支持,Spring 就提供了针对虚拟线程的 Executor,我们相信在这个方面会有更多的进展。
李三红: 这确实是一个老大难的问题。正如我们刚才所说,Java 语言本身的创新越来越快了。就像 InfoQ 2023 年的 Java 趋势报告[2] 所示,目前主流市场采用的还是 JDK 8 和 JDK 11,而最新的 JDK 版本已经到了 JDK 21,中间的差距是很大的。这对于企业来讲,也是一个非常大的矛盾,因为 OpenJDK 社区很多的参与者,像 ARM、Intel 这样的芯片厂商,都会基于最新的 JDK 版本做优化和支持,如果企业内部使用比较旧的版本,就会导致我们难以享受这样的性能红利。
至于阻碍升级的原因,从阿里这边的经验来看,从 JDK 8 到 JDK 11、JDK 17 和 JDK 21 这样的一个跃进,本身有很多兼容性问题,我相信技术视角与业务视角是有些冲突的。比如,作为业务架构师,我可能最优先考虑的是升级之后,底线要保证业务的连续性,不能因为升级带来稳定性事故。但是这可能只是一种外在表现,本质其实在于,在业务迭代很快的情况下,我们很多的底层架构本身对版本升级的容忍度没有设计得那么完整,比如是否有健全的单元测试,是否对开源库依赖有很好的版本收敛管理,是否有健全的灰度和监控系统,这都决定了是否能够很容易地进行升级。如果代码有很好的单元测试覆盖,开源库版本得到了很好的收敛和控制,有很好的灰度系统,我相信业务部门也会很想去升级的。所以,本质因素还是在于底层架构做的够不够好。
对于 Java 升级,这里也给大家推荐一个工具 - Eclipse Migration Tool for Java(EMT4J),由阿里开源,目前在 Eclipse 基金会 Adoptium 下孵化。初衷是希望把 Java 版本升级的专家经验沉淀到这个工具,帮助 Java 开发者可以更快地升级到新的 Java 版本。
InfoQ:对的,可能本质还是在于我们底层的一些工程实践有没有做好。其实在我们的业务实践中,还有一种场景就是一些安全漏洞,像 Spring 逐渐会在更新的版本上去解决,较旧的版本不再维护,这也促使重视安全漏洞的公司不得不去升级 Spring 版本,进而带动 JDK 版本的升级。
李三红: 云计算里面有个非常关键的概念叫做弹性,即“现用现付”(pay-as-you-go) 的商业模式,通过“按需”的原则来提供弹性的资源。在没有用户请求的时候,不占用任何资源,而在请求到来的时候,再去启动实例资源处理请求。这样的场景对 Java 的冷启动提出了很大的挑战。
针对 Java 冷启动这个问题,我觉得可以从三个技术维度来阐述。
第一个就是百分百兼容 Java 标准的技术,它对 Java 应用没有侵入,使用之后就能对应用启动进行加速。比如说 AppCDS。它本质上需要将 Java 应用先运行一遍,跑完之后,我们把它使用了哪些 class 给 dump 出来,第一遍运行的过程叫做 trace。在后续第二遍运行的时候,因为已经知道了要加载哪些类,只需 replay 即可。它的好处在于完全兼容 Java,对业务代码无侵入,但是对运维和 DevOps 侵入比较大。
第二个方向就是原生镜像(native image),即 GraalVM。它有一个封闭性假设(close word assumption),它会把用到的所有的类进行静态编译,就像 C++ 一样,这样就可以提高启动速度。它的问题在于,虽然 Java 是一个静态类型语言,但是它有很多的动态特性,比如反射、类的动态加载等,它们与原生镜像不兼容,如果使用 GraalVM 原生镜像的话,会导致一些预料之外的行为,因此这种方式对 Java 应用会有一定的侵入性。
第三个方向,叫做检查点和恢复(checkpoint-restore),以 OpenJDK CRaC(Coordinated Restore at Checkpoint)项目为代表。这种方式就是预先生成一个快照,如果新的请求进来,快速拉起快照即可。这种方式的问题在于,我们一般的 Java 应用都是 stateful style 编写的,它对状态的处理会比较困难。比如在 Java 应用中,我们要生成随机数或者递增的计数器,在恢复之后就可能会出错。
Java 业界大致就是这三个方向,目前都在各自的道路上演进。而在代表着 Java 标准方面的演进,OpenJDK 社区提出了 Leyden 项目[3]。Leyden 会从 Java 标准的层面(Java 语言以及虚拟机标准)解决 Java 启动的问题,在 Java 层面 Leyden 引入了”Static Image”概念。
李三红: 的确,Spring 现在基本处于主导的地位。目前也有其他的一些框架,比如 Quarkus、Micronaut 等。以 Quarkus 为例,它是红帽推出的框架。阿里是 GraalVM Project Advisory Board 的成员,在 GraalVM 社区层面,我们也有一些关于 Quarkus 的交流。Quarkus 明确提出了自己的设计哲学,就是容器优先(Container First),针对 Java 的启动时间和内存使用进行优化。Quarkus 的很多设计原则,有助于让我们思考如何去实现中间件,面向云原生解决 Java 的问题,所以,我们需要关注的是:
一方面它致力于在框架层面解决云原生诉求的问题,比如它提供了 fast-jar 的概念,通过在构建期提前计算好索引,解决 Java 类加载比较慢的问题。
另一面在底层它考虑如何更好地结合类似 GraalVM/Native Image、CRaC 这样的技术。
目前来看,Spring 是一个老牌的框架,拥有很稳定的市场地位,而且也在不断演进,比如它的 Spring Native 相关技术,很难说未来谁能颠覆它。但是,不同的框架互相借鉴和学习,对 Java 开发者是一件好事,我们能够拥有丰富的软件生态支持。
李三红: 我想讲一下对 Java 整体发展的观察。阿里作为 Java 标准委员会 JCP-EC 成员,2023 年四月份在阿里新加坡办公室组织了一次 JCP EC 专家委员会线下的闭门会议,探讨了 Java 的未来发展。谈谈我对这次会议的感受。
从一个开发者的视角来看 Java 发展可以分为两个方向,一个叫 Scaling Up,一个是 Scaling Down,分别指的是 Java 技术在功能方面往上演进以及在普及易用方面往下演进,也就是兼顾更广的人群。
我们先说第一点(Scaling Up)。大家都知道 Java 在处理大型的、复杂的、跨团队合作的项目是有其独特的优势的。在软件开发阶段,借助以康威定律为理论基础的微服务最佳实践,Java 可以帮助一个复杂的大型组织极大释放各个团队的并行研发效率。而在软件生产阶段,Java 给开发者提供了丰富的技术手段,从基础的 JFR(low-overhead JVM profiling 技术)、BCI(Bytecode Instrument)、JMX 到上层的各种监控、探针技术,极大提高了线上 Java 应用,尤其是大规模部署集群的可观测性。同时,大量的 Java 性能诊断、问题排查工具,都可以快速有效地帮助开发者解决生产环境碰到的问题。
由 Oracle 主导的 OpenJDK 社区发起的四大项目(Four Big Initiatives),即:Loom、Valhalla、Panama 和 Amber。前三个项目就和 Java 技术的 Scaling Up 方向演进直接相关。Loom 我们前面讨论虚拟线程的时候涉及到了,我们再展开聊聊 Valhalla。Valhalla 的目标是为 Java 增加 Value objects、Primitive classes,以及 Specialized generics 的支持。大家都知道,在 Java 中除了八种基础的 primitive data types,一切皆对象。Java 对象除了增加了额外的 footprint 负担(对象头), 还引入了通过对象指针(JVM 内部表示)的数据间接访问的性能代价。这涉及到计算机体系结构领域被反复提到的一个概念,叫做内存墙(Memory Wall)。在 80 年代、90 年代早期,CPU 去访问内存和在 CPU 内进行计算的代价是差不多一个数量级的。Java 是 90 年代初设计出来的,Java 对象內部实现依赖了大量的间接指针。就 80、90 年代的硬件而言,相比 CPU 内计算,访问内存的代价也许是可以接受的。但是对于现代的硬件体系结构而言,CPU 访问内存相对于执行计算的代价,一次 cache miss 的相对代价是相当高的。如何更高效地访问内存数据结构,就是 Valhalla 致力于解决的问题,包括它提出的原始类型以及如何对指针结构进行扁平化,避免层级查找。整体上在 Scaling Up 方面的发展,Java 一直在致力于思考如何更好地服务面向企业级的计算,以及更好地服务于大规模分布式的场景。
而第二个方面就是 所谓的 Scaling Down,Java 也很关注像学生群体学习 Java 语言本身的入门难度问题。因为相对于 Python 这样的语言,Java 的学习门槛会比较高,需要先了解面向对象编程,要学会编写一个 static main 函数,这对于初学者, 尤其是面向低年龄段比如中小学生,它的学习曲线仍然比较高。Java 目前比较关注这个问题,在 JDK21(JEP 445 [4])和 JDK 22(JEP 463 [5])中做了一些改进,使得 Java 能够像 Python 一样,很简单就能把入门程序写出来。
李三红: 今天 AI 确实是比较火,突然间就爆发了。其实,它本身对 AI 基础设施的影响还是比较大的。鉴于 GPU 卡的价格还是比较昂贵,不管是推理还是训练的成本都很高。这对整个 AI 基础软件的效率和性能优化提出了很大的挑战,也就是如何更高效地利用底层的 AI 算力,实现最大的性价比。
现在,市场上主流的可能还是以英伟达的 GPU 卡为主,而软件方面基本以 CUDA 生态为主导。CUDA 在 2007 年发布,CUDA 不仅是一种编程语言,也包含它背后的高性能编译系统,以及近十几年围绕 CUDA 构建的软件生态(一系列高性能函数库等)。
但对于开发者而言,使用 CUDA 编程去释放 GPU 潜力的学习门槛也是比较高的。AI 领域还有一些 AI 编译器(ML Compiler),它们的目标也是让 AI 模型更高效,也更好地利用底层异构平台的算力,降低手写 CUDA 的代价。当然,很多有经验的工程师手动编写的 CUDA 代码要比 AI 编译器生成的代码好得多,这也考验 AI 编译器的自动编译能力,是否能够更大化释放底层的AI算力,这是它所面临的挑战。
除此之外,AI 领域的硬件架构碎片化 也比较严重,是典型的昆虫纲悖论问题。它不像通用编程语言 Java、Go 在数据中心使用的 CPU 架构,相对统一,主流的就是 X86、Arm 等这么几种架构类型。
2023 年 AI 爆发,像我们前面说的,对 AI 算力的提升提出了很高的要求。所以我们期望能够从编程语言或编程系统去释放底层并行硬件的算力,这本身也是编程语言应该承载的东西。
在 2023 年的编程语言层面,值得关注下 Mojo,它是 LLVM 的作者 Chris Lattner 提出的,目前还处于一个很早期的开发阶段。从公开的资料,我们能够看到它想解决的问题:
第一个问题就是所谓的“两个世界的问题”(Two-world Problem),Python 与高性能的 C、C++ 代码互操作带来的系统复杂性。Mojo 可以认为是 Python 的超集,具有 Python 的易用性,同时又具备 C/C++ 的高性能。
第二问题就是 CUDA 是针对英伟达硬件的软件生态系统,CUDA 有自己的局限性,比如缺乏一致的 debuggers、profilers 工具支持,被绑定在特定的硬件厂商。Mojo 以及背后的 Modular 公司有可能想去解决 Three-world 或 N-world 的问题。借助一门编程语言以及更加开放的生态,能够安全地去释放整个异构 GPU 的算力问题,这还是值得期待的。
李三红: 这块我也没有太直观的感受。目前,比较值得关注的是微软 Copilot 的自动代码生成功能。我认为,AI 和自动代码生成在可预见的未来,有可能释放程序员的开发性工作,大大提升开发人员的工作效率。
InfoQ:是的,目前业内都在做一些相关的尝试。使用自然语言直接编程也许还有一些难度,但是业界的一些实践确实会带来我们开发人员工作效率的提升。非常感谢李老师接受我们的采访,并分享您对 2023 年编程语言领域的见解。
参考链接:
[1] https://www.zdnet.com/article/chrome-70-of-all-security-bugs-are-memory-safety-issues/
[2] https://www.infoq.com/articles/java-trends-report-2023/ (译文链接:https://www.infoq.cn/article/PgTo5YAyrPszGXHiTbss)
[3] https://openjdk.java.net/projects/leyden/
[4] https://openjdk.org/jeps/445
[5] https://openjdk.org/jeps/463
如果你觉得本文对你有帮助,或者你对 Java 等编程语言在大模型时代的发展有自己的思考,欢迎在文末留言告诉我们!
在 2023 年结束之际,InfoQ 编辑部重磅推出“年度技术盘点与展望”专题,聚焦 AIGC 引发的变革,与 50 多位头部专家深度对话,希望能为你揭示架构、前端、运维、大数据、云计算、编程语言、数据库等领域的核心变化和演进逻辑,明晰金融、汽车、制造、零售等行业的数字化、大模型应用思路和路径。
谷歌新年大裁员,引硅谷裁员潮!OpenAI正式推出GPT Store,但第一批应用已被像素级抄袭;腾讯服务器深夜崩溃 | Q资讯
系统 bug 致百人入狱,砸了 2.8 亿元仍上云失败!二十年了,这家大企业被日本软件坑惨了
2024 年 4 月 18-20 日,QCon 全球软件开发大会·北京站将作为 InfoQ 首场会议,重磅亮相!本届 QCon 大会推出全新主题——全面进化,并策划了大模型场景化落地、大模型产品设计、大模型推理加速、高质量架构、单体 vs 微服务、可观测、性能优化、下一代生产力工具、开源产品的商业闭环、最新编程语言、数据质量与治理、大前端前沿技术、自研 OS 时代的大终端等超多精彩专题。
仅限 1 月,可享 7 折特惠购票,立减 2040 元,咨询购票可联系票务经理 18514549229 。目前大会议题同步征集中,点击「阅读原文」即可查看详情,期待与各位开发者现场交流。
微信扫码关注该文公众号作者