为何Jenkins中使用StringJoiner后readYaml功能失效?
问题场景
Pipeline脚本
import groovy.json.* pipeline { agent { node { label 'mycomputer' } } stages { stage ( 'Test') { steps{ script { StringJoiner a = new StringJoiner(","); a.add("a").add("b").add("c") env.out = a.toString() echo env.out datas = readYaml file: "/home/cloud-user/test.yaml" } } } } }
执行输出
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Test)
[Pipeline] script
[Pipeline] {
[Pipeline] echo
a,b,c
[Pipeline] readYaml
[Pipeline] }
[Pipeline] // script
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] End of Pipeline
异常信息如下:
in field com.cloudbees.groovy.cps.impl.BlockScopeEnv.locals
in object com.cloudbees.groovy.cps.impl.BlockScopeEnv@7bb36fb8
...
核心异常:Caused: java.io.NotSerializableException: java.util.StringJoiner
Finished: FAILURE
YAML文件内容
--- HELLO: "WORLD"
用户疑问:移除readYaml命令后错误消失,为何看似无关的readYaml调用会触发StringJoiner的序列化操作?
原因解释
Jenkins Pipeline基于CPS(Continuation-Passing Style)机制运行,这个机制要求当前作用域内的所有变量都必须是可序列化的——因为流水线需要在步骤执行的间隙序列化整个执行上下文,以支持断点续跑、跨节点执行等特性。
你的代码中,StringJoiner a是一个不可序列化的Java类。在没有调用readYaml时,脚本块执行到echo后,变量a虽然还存在于局部作用域,但Jenkins可能没有触发完整的上下文序列化检查,或者变量被快速回收,因此没有抛出异常。
而当调用readYaml时,这个步骤属于Jenkins的内置流水线步骤,执行它会触发CPS机制对当前脚本块的作用域进行序列化快照。此时,局部变量a还存在于当前的作用域变量表中(即使已经用完),Jenkins尝试序列化它时,就会抛出NotSerializableException。
解决办法
- 用完
StringJoiner后手动将其置为null,让GC回收,避免被序列化:StringJoiner a = new StringJoiner(","); a.add("a").add("b").add("c") env.out = a.toString() a = null // 手动置空 echo env.out datas = readYaml file: "/home/cloud-user/test.yaml" - 改用Groovy原生的可序列化方式拼接字符串,比如直接用集合的
join方法:env.out = ["a", "b", "c"].join(",") echo env.out datas = readYaml file: "/home/cloud-user/test.yaml"
内容的提问来源于stack exchange,提问作者Bryan Tan

