技术随笔-202501-Java作者Gosling聊Java

98 阅读21分钟

原文在这里: Java三十周年,Gosling深度访谈:修补C++造就Java、泛型备受诟病

为技术写历史,回顾技术发展史是一件非常有意思的事情,今天聊聊Java作者对Java发展和职业发展的看法。

挑几个重要的内容节选:

摘选1:

现在还参与多少 Java 开发?

主持人:你现在还参与讨论吗?

詹姆斯·高斯林:有时候会。

主持人:你正式退休了吗?

詹姆斯·高斯林:我现在正式退休了,无业游民一个。除了我妻子,谁的话我都不用听。

主持人:那么 Java 对你来说,如今还占据多大的分量呢?

詹姆斯·高斯林:其实不多了。我离开 Java 组织已经 15 年了。现在照管它的人们,我非常尊敬他们,他们非常出色。而且社区也在约束着他们,这很棒。我通常不是那种喜欢到处对人大喊大叫的人。

摘选2,解释Java为什么早期不开源的原因:

主持人:如果回顾你开始 Java 的这 30 年,你能指出几个重要的时刻吗?可能有很多,但你能挑几个吗?

詹姆斯·高斯林:有几个“显而易见”的关键时刻。比如 Java 的正式发布,再比如我们当时跟网景(Netscape)那种疯狂的合作关系,还有与整个行业中其他势力之间的一些微妙互动。

我们也因为没有更早地开源而受到不少批评。其实从一开始我们就提供了源代码,但用的不是开源社区真正推崇的那种许可证。这里面牵涉很多复杂的问题,但从我们的角度来说,最大的担忧是:当时有好几家非常强劲的竞争对手,他们几乎是铁了心要“干掉我们”。如果我们早早使用一个完全开放的许可证,那无异于把武器递到对手手里,简直像是发了一张“开火许可证”。

我们当时预估,大概有五六家公司在盯着我们,只等我们一开放代码,就要把我们碾碎。所以我们处在一个非常矛盾的局面里:理智上知道开源可能对发展更有利,但情势又逼着我们不得不防守。这不是个容易做的决定。

主持人:听起来那时候也有很多“戏剧性”的瞬间?

詹姆斯·高斯林:有的,甚至可以说比电视里的剧情还精彩。比如,有一次我们正与 ECMA(欧洲计算机制造商协会)谈判,想把 Java 纳入为他们的一个标准。当时的气氛看起来很顺利,大家在技术细节上已经基本达成一致。

但我们不知道的是,有三家大型公司(我就不点名了,你大概也能猜到是哪几家)一直在暗中跟 ECMA 协调。等我们准备好召开一次会议、敲定标准时,ECMA 的代表突然变脸——就在前一天晚上我们还一起吃饭、意见完全一致——却在会上宣布:新计划是把 Java 分成三部分,语言、虚拟机和标准库,每一部分由那三家公司中的一家负责,而 Sun 公司将不再在标准制定中拥有任何正式地位。

换句话说,那三家公司将分别掌控 Java 的三大核心组成,而我们这个发明者彻底被排除在外。这对我们来说简直是晴天霹雳。

更关键的是,Java 是一个生态系统,不是几段独立的代码拼凑起来的东西。每一个部分都会影响其他所有部分。

摘选3,Java的发布版本。俗话说,你发任你发,我用Java8。要是Java永远不更新就好了!!

主持人:Java 目前的运作机制是否良好?它现在的发布节奏靠谱吗?你觉得它未来 30 年会朝着正确方向发展吗?

詹姆斯·高斯林:我其实一直对它运转得这么好感到有些惊讶。

主持人:你是指六个月发布一次的节奏运作得不错?

詹姆斯·高斯林:其实我并不觉得六个月的发布周期带来了什么本质变化。在这之前,Java 是三年左右发布一个大版本,但在这三年之间,也会不断推出构建版本(builds),很多人,包括我自己,其实都在使用这些中间版本。而这些构建版本通常也都非常稳定。

主持人:像现在我们在两个长期支持版本(LTS)之间,也会用 23、24 这样的常规版本。

詹姆斯·高斯林:对,是这样。现在是每六个月发布一次版本,而 LTS 版本是每两年发布一次,对吧?

主持人:对的,是每两年一个 LTS。

摘选4,正义的底气,你必须要有Fukc you money

主持人:一个完全不同的话题。我在你 LinkedIn 的一条消息中看到,你也要求人们思考自己的工作及其责任。如果你在一家公司担任程序员,被要求做一些你认为邪恶的事情,比如奇怪的营销技巧之类的。作为开发者,我们对如何使用我们的工具和开发者能力负有多大责任?

詹姆斯·高斯林:我一直坚信,如果你的公司要求你做一些不道德的事,你应该直接走人。

当然,首先你也可以和他们谈一谈:“你们真的想这么做吗?你们真的理解这么做的后果吗?”有时候,你可能会让他们意识到:“哦,你说得对,我们不该这样对待客户。我们确实没考虑到长期影响。”但更多的时候,他们只会说:“哦,是的,没事的。”

你会遇到一些曾在医保公司工作过的人,听到他们讲的一些故事真的让人震惊,比如:“他们居然会那样做?”

这其实也和现实情况有关。如果是在就业机会很多的时候——软件工程师过去就经常处在这种环境下——那么说走就走也很容易。

主持人:没错,的确如此。

詹姆斯·高斯林:现在就不一样了。现在的就业市场非常非常艰难,尤其是在美国。美国现在简直就是一团糟。

我在 LinkedIn 上写的那篇文章,谈的是“叫板的底气钱”(fuck you money)这个概念——这个词其实已经存在几个世纪了。意思是说:你是否有足够的积蓄,能在必要的时候对雇主说“去你的”,然后直接走人?如果你没有这份底气钱,你在某种程度上就成了奴隶。

而“奴役”也可以以别的形式存在。比如说,如果你是一个家长,孩子又有严重的健康问题,那么考虑到美国医疗体系的运作方式,一旦你离职,可能就会影响到孩子的治疗和健康。从雇主的角度看,这反倒成了美国糟糕医疗体系的一种“优势”——它让员工在某种程度上被绑定在公司里。这也许在技术上算不上真正的奴役,但感觉真的非常接近了

摘选4: 如何看待AI编程?

主持人:随着人工智能带来的全面竞争,选择会不会变得更加艰难?对于很多总监、工程师、很多老板来说,他们认为他们不再需要软件开发者了。

詹姆斯·高斯林:我认为他们是在自欺欺人。而且我觉得,有些情况简直近乎滑稽。一些人工智能领域的人,比如 OpenAI 这样的公司一直在说:“啊,你不需要文案撰稿人了,你不需要这个那个了,因为人工智能可以做所有事情。”但现实是,AI 目前能做的事情非常有限,效果也往往不如人意。

比如有家律所尝试用 AI 来撰写法律文书,结果文书看起来像那么回事,但内容完全是胡扯,最后还因此受到了处罚。这类例子比比皆是。

但软件工程又不太一样。它有一个独特的特性,就是我们有“库”这个概念。如果某件事你做过一次,下次你很可能就会把它封装成一个库,以后复用。很多人用的东西还会变成开源库。换句话说,开发者并不总是在重复造轮子,除非你还在学习阶段。

AI 工具,尤其是像 ChatGPT 这样的生成式系统,本质上是基于大量已有代码训练的,它们在“插值”方面表现不错——也就是在已有知识的基础上做填充和改写。但这并不是软件开发的难点。

真正有挑战的是“外推”——也就是解决那些没人遇到过的新问题。软件工程的乐趣正是你经常在做全新的东西,而不是一遍遍复制已有的方案。

我喜欢拿土木工程打比方:你建一座桥要花很多精力,但建下一座桥、再下一座桥,成本都很高。而在软件工程里,一旦你写好了一个模块,复制它几乎是免费的。这种“从零到一”的创造,是 AI 目前很难替代的。

我看到的像 ChatGPT 这样的工具的用途,主要是在学习方面。我发现自己把它当作一种自动化的帮助系统。就像,“我该如何写一段代码来连接这个和那个?”它有一定的准确率。

摘要5:Java的内存管理

至于垃圾回收器,它的表现已经好得离谱。

主持人:是的,我刚加入 Azul 的时候,最让我惊讶的就是这一点。我当时接到的第一个任务,就是写一篇关于不同垃圾回收器的博客文章。虽然我已经做了十多年 Java 开发了,但从来没有为垃圾回收操过心,因为它总是能“自动”处理好。

但当我写那篇文章,开始和那些亲自实现各种垃圾回收器的人聊之后,我才意识到,这背后有多么复杂的技术体系!有那么多专业知识和工程细节,都是为了让开发者根本不需要去考虑垃圾回收本身。这才是真正了不起的地方。

詹姆斯·高斯林:没错,垃圾回收器令人惊叹的地方正在于此——它既消除了大量复杂性,又容纳了巨大的系统复杂性。

很多人都喜欢 Rust 的内存管理,我自己其实也挺喜欢的。但问题是,一旦你的数据结构变得复杂,比如有很多交叉引用、缓存层级等等,Rust 的模型就会开始变得吃力。而 Java 的垃圾回收器则能把这些复杂的数据结构处理得井井有条,而且运行效率极高。

我现在还会碰到一些人,他们说垃圾回收太慢了,一次回收得几分钟……

拜托,抱歉,几十年来,垃圾回收都不需要几分钟了。

主持人:很多批评 Java 的人,其实是很久以前用过 Java,对后来社区的贡献和这些年版本的改进完全不了解。

詹姆斯·高斯林:时下,一个中规中矩的垃圾回收器都能把最大暂停时间控制在一秒以下。而真正优秀的垃圾回收器,最大暂停时间可以压到 5 到 10 毫秒之间。

当然了,之所以存在多种不同的垃圾回收器,正是因为它们在吞吐量和延迟之间做出了不同的权衡。如果你想让系统负载更低,那就得接受响应时间可能会更不平稳。就像我们刚才说的哈希表例子一样——你想让它“超级快”,那就得接受偶尔会有性能波动。你可以通过各种算法来平滑这些波动,但无法真正消除它们。

不过也有一些回收器,能做到完全没有性能抖动,但代价就是吞吐量会被拉下来。

三十年前,如果你想获得一致的响应时间或低延迟,代价可能是 30% 的吞吐量损失。而现在,如果你要实现非常低的延迟,吞吐量的牺牲只需要 1%-2%。

垃圾回收是计算机领域里最不显眼、但也是最精彩的技术之一。当你开始读关于垃圾回收的学术论文时——我的天啊,那真是一个奇妙纷呈的世界。

主持人:而且令人惊奇的是,尽管系统变得越来越大,使用的内存越来越多,它们却改进了这么多。

詹姆斯·高斯林:另一方面,我们也有了更多的 CPU 作为底层支撑,所以所有这些东西的进化速度真是惊人。这在某种程度上也是 Java 在嵌入式系统上运行时偏离初衷的地方。在服务器世界,没有哪个系统会只有 1G 内存。如果你买一台新的树莓派,通常有 8 到 16G 内存。我面前这台新的台式电脑有 256G 内存。

当然,所有这些内存都是为了施展一些能让程序运行更快的技巧。如果你想适应小内存环境,有些事情你就做不了,因为你没有足够的空间。同时,大量的内存也可以掩盖某些工程上的草率。

詹姆斯·高斯林:几年前我做的一个项目中,最让我头疼的问题是它占用了大量内存。深入调查后我们发现,虽然表面上看起来是 Java 虚拟机占了很多内存,但其实真正消耗内存的是那些引入的库。Java 生态系统的一大乐趣在于,几乎所有东西都有丰富的选择。但这也是最大的痛苦之一。

摘选6:Java语言设计的取舍

主持人:Java 中是否有很多类似的事情让你觉得:“我犯了个错误?”人们可能会说是空指针或空指针异常。那是个错误吗?

詹姆斯·高斯林:很多这类事情,就像是在玩“打地鼠”游戏。你知道那种你要敲打冒出来的地鼠的游戏。工程就是这样。而且总是有权衡取舍。我们最擅长处理的是工程上的权衡,但有时社会学层面的权衡更难。

我经常被诟病的一件事是泛型。94 年的时候,关于泛型有过一场大争论。有些人坚持:“必须要有泛型。”我当时想:“好吧。”因为这些人用过泛型,他们来自 C++ 世界。“但是 C++ 的泛型糟透了。我们必须把它做对。”

然后问题就来了,什么是“对”的?我跟很多人聊过。编程语言中的泛型,在当时是一个重要的研究领域,但对于何为“良策”,众说纷纭,莫衷一是。

有一派人说:“在我们搞定泛型之前,什么都不该发布。”但很明显,这会耗费我们两三年的时间。如果我们推迟两三年,互联网正在爆发式增长,那可是爆发的极早期阶段。如果我们等上几年把它做好,我们就会错过整个浪潮。如果我随便塞进去一个东西,结果是错的——我估计有 99% 的可能性——那么撤销它将会非常困难。当时有很多面向对象的编程语言没有泛型,它们也运行得很好。

最终,几乎形成了一场关于 Java 泛型应该如何实现的全球竞赛。一个大问题是它如何与内省(introspection)交互。没有人能提出一个可行的方案。这是一个关于所谓“具体化”(reification)的争论。如果我们不惜破坏当时存在的每一个应用程序,我们本可以实现具体化。

主持人:我认为这完全违背了 Java 的哲学。

詹姆斯·高斯林:当你拥有一个庞大的用户社区时,你就肩负着重大的责任,不能把他们搞砸。直到今天,我还会从朋友那里听到:“那是个错误。”而我会说,不,那不是错误,那是一种妥协。

主持人: 作为 Java 的创始人和最初的创造者,你当时的工作方式与现在 Java 通过 JEP 和六个月发布周期的演进方式之间,是否存在巨大差异?我猜存在的。

詹姆斯·高斯林:完全不同,天壤之别。在发布之前,我可以在一个下午做出翻天覆地的改动。没什么大不了的,轻而易举。而且我经常这么做。有一天我删掉了 goto,因为我实在受够了它的愚蠢。我默认继承了 C 和 C++ 的很多东西,以便让人们感到熟悉。但是 goto 是那种会导致各种奇怪边界情况的东西。一个下午,我真的就是不耐烦了,做了一些有限的研究,然后“砰”,它就消失了。那是一段极其奢侈的时光。

硅谷流行着一个令我情感极为复杂的工程原则,你经常从埃隆·马斯克和马克·扎克伯格这样的人口中听到:“快速行动,打破陈规。”当你在构建原型时,我完全赞成。但一旦有了用户,他们依赖你的产品,游戏规则就完全变了。我对“快速行动”本身没意见。

主持人: 但不是破坏。

詹姆斯·高斯林: 但不是破坏。而且,“快速行动”常常被曲解为随心所欲地做些蠢事,看看人们会怎么抱怨。这对用户体验来说可不妙。

摘选7: 强调安全性

Java 的诞生:原本只是想修修 C++ 的边界问题

主持人:5 月 23 日是 Java 首个 Beta 版于 1995 年发布的三十周年纪念日,我们荣幸地推出了史上首次单嘉宾播客。而这位嘉宾,分量十足——他就是 Java 的缔造者,詹姆斯·高斯林。你近来生活如何?

詹姆斯·高斯林: 其实,那更像是 35 年前的事了……

主持人:或许我们应该把时光倒回 35 年前。你当初为何以及如何开启这项事业的呢?

詹姆斯·高斯林:故事始于 90 年末、91 年初。我们一群人注意到,计算机技术正渗透到各个角落。它不再仅仅局限于数据中心、科研人员的办公桌之类的场景。我们感觉到数字系统正在扩张,当时我们对此相当敏感,但似乎整个行业,包括 Sun 公司,对此却视而不见。

于是斯科特(Sun 公司共同创办者之一)说:“好吧,你们这些爱发牢骚的家伙,去研究研究这个吧。”

我们就去了。我们做的一件事就是走访各行各业的人士,了解他们如何在自己的产品中运用计算机。从录像机制造商到电梯、火车头的工程师,我们进行了大量的实地考察。那几个月四处拜访,会见欧洲、亚洲那些正在创造事物的人们,真是一段非常有趣的时光。

期间浮现出许多问题,有些着实令人沮丧。这些人的一个共同点是,他们都在将网络融入一切,但他们却在重新发明网络。他们做的那些发明,计算机科学在几十年前就已经做过,并且深知其为何会失败。他们大量使用串行线路,却没有足够的纠错机制。那些从事工业自动化的人还在用 8 位地址。天啊,8 位地址根本无法扩展,16 位也仍然不够。

然而,在与这些人讨论需求时,我们也从他们身上学到了东西。他们从不提及安全性和可靠性。并非因为他们不在乎,而是因为安全性和可靠性是如此至关重要,以至于根本没必要列在清单上,那就像说“哦,我们要盖房子,得记得往里面放氧气”一样。这是一种不言而喻、绝无妥协余地的要求。

当时,性能是计算机设计的一切。为了提升一点性能而牺牲一点可靠性是很常见的做法。那个时代我最爱举的例子是除法的实现方式:他们用牛顿迭代法求解器,而不是你在高中学到的长除法(或者至少是其电子版)。所以他们的除法运算非常非常快,但低位精度却有些马虎。人们总是这样做。即便在 Sun 公司内部,人们也会做出一些让我们这些人感到不安的权衡。对很多用户而言,这可是人命关天。如果你在为火车头或电梯构建控制系统,一旦出错,通常就会血溅当场。第一要义,绝不能把客户给“干掉”。

主持人:这确实是条铁律。但我很惊讶你提到火车头,而如今使用 Java 的人,通常认为它是一种服务器语言,部署在大型服务器上。

詹姆斯·高斯林:这正是分歧之处的全部来源。两个方向其实都对。企业市场有更多的资金,因此 Sun 公司最终将大部分投资投入到这方面。这种发展轨迹多少有点出乎意料。但尽管如此,当你回头看看人们实际用 Web 服务做的那些事——无论是航班调度、打车服务,还是城市公共交通系统的运营——我们最初想象的是像电梯这种规模的小型设备,而其他人则在构想类似伦敦地铁这样的大系统。虽然规模不同,但如果系统崩溃,后果可能是一样严重,甚至更糟。所以,一旦出现故障,代价会非常惨重。

我对 C 语言最不满意的一点,就是它太容易写出难以修复的错误,而这些错误往往会带来非常隐蔽、难以诊断的问题。边界条件实在太多了。

当我们启动这个项目时,我们首先去调研用户到底在做些什么,理解他们的工作方式。然后我们开始构建原型——因为我们是工程师,更擅长用代码说话,而不是写白皮书。原型的一个优势在于,它迫使你在脑中真正具体化问题。我们当时有几位软件工程师、几位硬件工程师,还有一位非常优秀的商业伙伴。

随着我们不断思考从用户那里收集到的需求清单,逐渐发现,软件工程中一些“标准做法”其实不断制造问题。最大的问题都集中在可靠性和安全性上。而这两个方面,在某种程度上其实是密切相关的,甚至可以说本质上是一回事。因为世界上很多安全攻击,本质上就是程序错误。当然,也有一些攻击是源于极端的愚蠢——比如把 root 密码设为空。你说这是个错误,还是彻头彻尾的无能?你自己判断吧。

我们一开始用的是 C,但很快就觉得不行……后来又尝试了 C++,但也发现问题依旧。

于是,我就成了团队里那个说“我们是不是该从方法论层面解决这些问题”的人。结果就是,一旦你开始尝试去解决这些方法论问题,很快就会被带着一步步走远。最初只是想修补 C++ 里那些讨厌的边界情况,没想到最后发展成了一件完全不同的事。