
人们在跨语言, 跨进程通信方面采用过不少的方案, 交换文件, CORBA, SOAP, 最后得到最广泛应用的是 RESTful API, 交换格式通常用文本格式的 JSON 和 XML. 但作为更高效的通信还是二进制格式, 在 Java 方面, 有过 Java 内置的 RMI, Spring 的 Hessian, Dubbo. 而发展到今天 gRPC 受到了更多的关注, gRPC 的通信协议是 HTTP/2, 编码格式是 Google 高效的 Protobuf.
gRPC 是由 Google 发起的一个远程调用框架, 是 gRPC Remote Procedure Calls 的缩写, 各处的解释是 g 最初是代表 Google, 现在只是像 YAML Ain't Markup Language 类似的命名, 说 g 不再代表 Google, 怎么听起来有点 既要又要的感觉, 怎么都会认为 gRPC 为 Google 的 RPC.
总之, gRPC 是一个高性能的开源统一的 RPC 框架, 能叫做统一是因为它支持多种语方, 如 Go, C++, Java, Python, Node, C#, PHP 等, 多语言支持是 由 Protobuf 格式决定的, 总之它是 Protobuf over HTTP/2, 实现上是用 Protobuf 定义数据结构与服务方法, 再映射成不同语言的代码实现. gRPC 已然成为了 RPC 的标准, 真正意识到它的不一般是在 Postman 中发现了它, 可见其在业界受不到了应用的重视.
Read More
Java 22 正式引入了 Foreign Function & Memory API, 简称 FFM API, 它源自于 Project Panama, 旨在用来替代 JNI, 提供更安全,健壮的本地动态库调用方式。之前在一文 Java 22 新特性学习 中跳过了对它的体验, 因为直接用 FFM API 来调用本地代码的话,写起来比 JNI 还更复杂,更别提和 JNA 对比,性能上 FFM API 大致与 JNI 相当,但总比 JNA 要好些, 原因是 JNA 使用了一个与系统相关的中间层动态库。像这种调用本地代码的场景,性能的差异基本体现在本地代码上,调用过程的性能差异一般可以忽略不计.
那时没深入 FFM API 的原因是它用起来过于复杂,这种复杂性应该由第三方库来屏蔽,可是自 Java 22 于 2024-03-19 发布以来目前仍未找到一款类似于 JNA 屏蔽了 JNI 那样的基于 FFM 的第三方库。所以还是事先来体验一下 FFM API 的开发过程,现在可以借助 AI 来引导学习, 并在后面与 JNA 作个性能对比, 只供参考。
Read More
本想用一篇日志记录下在 Docker 容器中使用 Java 调用 C++ 动态库时,当 C++ Crash 时如何自动生成 core dump, 不想分成了至少三篇来完成这一研究。 可以回顾一下前两篇日志
本文是基于第二篇进一步推进,继续探索如何在 Docker 容器中 Java 调用 C++ 动态库时的 core dump 如何生成。首先测试的平台依然是 AWS EC2 实例, OS 为 Amazon Linux 2023, Docker 版本为 25.0.14。为叙事方便,本文所用代码与上篇一样,但还是重复一遍,省却了连接跳转。
Read More
在 Java 应用程序通过 JNA 调用 C++ 动态库时,C++ 代码运行在与 Java 同一进程中,当 C++ 代码 Crash 的时候,将会导致整个 Java 应用程序崩溃。 对于一个 Web 应用,这不是我们期望的结果,由于某一个请求输入的数据导致 C++ 代码崩溃了当前 Java 进程,从而造成该 Web 服务已接受到的所有请求全部失败, 这是非常糟糕的用户体验。如果是 Java 代码本身的异常我们可用 try/catch 进行保护,影响只限制在当前请求。如果是 C++ 代码崩溃的话,Java 应用程序无法捕获到这个异常,以致于整个 Java 应用程序崩溃,甚至发生这种情况时连 hs_err.log 文件都来不及生成,更别说生成 HeapDump, 或 CoreDump 了。
如果是用原始 JNI 的方式来调用动态库,我们还能在 JNI 相关的 C++ 代码中捕获到异常,并抛给 Java 去处理。而用 JNA, 我们贪图了它的方便, 比如一个 Java 进程中同时加载同一接口的不同动态库版本(JNI 要同样的实现必须用自定义的 ClassLoader),但在 C++ 代码崩溃时, Java 就显得无能为力了, 只能跟随着立即死亡, 并且在控制台下找不到关于 C++ 因何失败的线索。比如 C++ 中内存被多次释放,或地址越界访问破坏了内存数据等。
Read More
本文初衷是为了解决 Java 应用程序通过 JNA 调用 C++ 动态库时,C++ 代码运行崩溃导致整个 Java 应用程序崩溃而进行的研究。从一个 C++ 调用 C++ 写的动态库起步,记录它在什么情况下产生 core dump 文件,如何分析 core dump 文件等过程。可惜篇幅无法控制,不足以再加入 Java->JNA->C++ 动态库内容了,所以不得不单列此篇,并更名为 'C++ 调用 C++ 动态库时问题诊断'. 关于 Java JNA 到 C++ 的问题诊断只能另立一篇了。
下面我们来用 JNA 的方式来调用 C++ 动态库,演示当 C++ 代码崩溃时会发生什么,并试图找到好的诊断办法。以下演示在 Linux 下进行, 并且 Linux 发行版是 Amazon Linux 2023.
Read More
Java 23 也是一个过渡版本, 还是到下面两个链接中找相应的更新
IntelliJ IDEA 对 Java 23 Language level 描述是
- 23 - Markdown document comments
- 23 (Preview) - Primitive types in patterns, implicitly declared classes, etc.
把上面第二个链接中的特性列出来
本文对上面用红点标记的特性重点关注
Read More
Java 22 是一个过渡版本, 还是到下面两个链接中找相应的更新
IntelliJ IDEA 对 Java 22 Language level 描述是
- 22 - Unnamed variables and patterns
- 22(Preview) - Statements before super(), string templates (2nd preview), etc.
把上面第二个链接中的特性列出来
本文对上面用红点标记的特性重点关注
Read More
迁移完所有的 WordPress 日志到 Hugo 之后, 终于有时间真正继承学习相关的新技术. Java 21 是于 2023 年 9 月份释放出来的 LTS 版本, 目前主要在用该版. Java 25 LTS 版本已发布, 按正常节奏应该要切换到该版本.
随着 AI 在编程界的花式表演, 所宣传的似乎就是要扑灭他人的学习热情, 编程方面越小白越好, 只要能写好小作文就行了. AI 当然还是要用, 但我对以往多少年传统的学习方式并不感到白花了心血. 告诉 AI 的一个课题 AI 确实能写出一篇漂亮, 规整的博客文章, 但其中有没有胡说八道, 只有试了才知道, 即使生成的文章无误, 也必须实践一遍才有更多更深的斩获.
如果没有相关的技术储备, 每次与 AI 互动的时候都要告诉它尽量用 Java 21 新特性, 因为新引入的特性基本能实现得更简洁, 高效, 估计 AI 才不那么在乎这些, 写出适于人阅读的代码恐怕不是 AI 的首要关注.
还是老办法, 关于 Java 某一版本新特性从两个链接出发
Read More
篇首说明: 本文十分冗长, 语言组织混乱, 如果觉得 TLDR, 就直接跳到 关于虚拟线程的总结 部分看要点, 若对总结上中的某些要素点仍有兴趣的话请倒查本文中其他部分的内容. 个人对 Java 虚拟线程的主动研究是为了在项目中更有效的使用它.
关于线程的概要
Java 21 于两年前 2023 年 9 月份放出,它是一个 LTS(long term support) 版本,个人基本就是把 LTS 当作能在正式项目中使用的版本。 Java 21 有几个增进编程体验的特性,像 Sequenced Collections, Record Patterns, 和 Pattern Matching for switch, 而对于性能改进的, 也是 Java 21 最具代表的特性无疑就是 Virtual Threads -- 虚拟线程。本文单列出它来,着重感受一下虚拟线程是什么,以及我们应该如何使用它。
其实在之前的 Java 19, 20 新特性学习 就有一定的笔墨介绍了于 Java 19 引入, Java 20 中尚处于第二次预览的虚拟线程。于其中大致体验了在一台 36 G 物理内存,默认堆内存为 9 G 的情况下, 创建 9000 个线程没问题,但要创建 10000 个线程就 OutOfMemoryError 了。而相同的环境下创建一百万个虚拟线程都没问题,没在继续往下试探了。
其实这种比较是没有意义的, Java 线程对应到平台线程的, 每个线程要至少实实在在的 2M 栈空间, 而一百万个虚拟线程相当于是创建了一百万个 Java 对象而已, 更像是相应数量的 Task, 实际运行时才由载体线程去调度执行 - (注: 后面所提到的载体线程和平台线程是同一个概念).
重新回顾一下何谓虚拟线程,Java 的虚拟线程实现是来自于 Project Loom 项目。与此相关的概念有线程,协程,以及纤程(Fiber),而虚拟线程对应的应该是纤程。
- 线程是操作系统最小的调度单位,每个线程有独立较大的栈空间(比如 2M),内核调度,切换开销大,可有效使用 CPU 多核
- 协程在单个线程内执行,共享线程栈空间或独立小空间,用户态调度,切换开销极小,但无法使用多核
- 纤程,介于线程与协程之间,很小的独立栈,用户态调度,切换开销较小。结合线程池,纤程可在线程间转移,这时岂不是要经内核态调度吗?
之所以把 Java 19 与 20 放一块是因为这两个版本都没有一个算得上正式的特性。都是些预览的,孵化中的,唯有一个支持 Linux 下 RISC-V 指令集与我们基本无关。所以 Java 19 和 Java 20 纯粹的过度版本,根本不该被正式项目采用,在 IntelliJ IDEA 中也是标它们为 No new language features。在我们的实践中正式项目只用 LTS 版。
还是分别从 https://openjdk.org/projects/jdk/19/ 和 https://openjdk.org/projects/jdk/20/ 抓关注点
从上面可以挑几个稍加了解,详细的介绍应该在学习 Java 21 时。它们是 Read MoreJava 19 新特性 Java 20 新特性