@southpolesteve: Zod 4.5 带来了内存占用的大幅下降。如果你正在使用 @Cloudflare Workers,你应该更新!

X AI KOLs Timeline 工具

摘要

Zod 4.5 实现了方法记忆化,以大幅减少内存占用,相比之前的版本,堆内存保留量减少了高达9.8倍。

Zod 4.5 带来了内存占用的大幅下降。如果你正在使用 @Cloudflare Workers,你应该更新!https://t.co/8fqmYCruBp
查看原文
查看缓存全文

缓存时间: 2026/08/30 22:24

Zod 4.5 大幅降低了内存使用。如果您正在 @Cloudflare Workers 上运行,请务必更新!
https://t.co/8fqmYCruBp


通过方法记忆化将 Zod 内存占用减少一个数量级

来源:https://zod.dev/blog/reducing-memory-footprint
Zod 4.5 实现了“方法记忆化”模式,允许其延迟为绑定方法分配内存,直到它们实际被使用时才进行分配(甚至可能永不使用)。本文将探讨该模式。

在 Zod 4.4 中,一个简单的 z.string() 会占用 7.5kb 堆内存。而在 Zod 4.5 中,仅占用 784 字节。

(图表描述:共享千字节轴上单个 schema 实例的堆占用,Zod 4.4.3 以灰色条形表示,4.5 版本尺寸以内部蓝色条形表示:10 键对象从 82.0kb 降至 11.0kb(7.4 倍),联合类型从 17.5kb 降至 2.13kb(8.3 倍),z.string().min(1) 从 16.7kb 降至 3.37kb(5.0 倍),记录类型从 16.4kb 降至 2.64kb(6.2 倍),z.string().optional() 从 12.6kb 降至 1.50kb(8.4 倍),字符串数组从 11.2kb 降至 1.93kb(5.8 倍),z.string() 从 7.53kb 降至 784b(9.8 倍),z.number() 从 4.44kb 降至 706b(6.4 倍);相比 4.4.3 版本内存占用最多减少 9.8 倍)

各 schema 实例堆占用对比,Zod 4.4.3 与 4.5(基准测试代码见:https://github.com/colinhacks/zod/blob/main/packages/bench/memory/schema-footprint.ts)

默认情况下,Zod schema 上的所有方法都采用自动绑定,这意味着它们不依赖隐式 this 语义来正常工作。这使得类似如下的代码模式得以实现,否则当依赖未绑定的原型方法时,可能会出现异常或不可预期的行为。

这种模式常用于防范某些特殊场景下的意外行为。npm 上有一个专门实现自动绑定功能的工具包。但其缺点在于:每个方法都会成为一个绑定闭包,从而占用内存空间。这使得同一个类的多个实例无法通过常规的原型链继承来共享方法。

如今,Zod 4.5 通过一种名为“方法记忆化”的模式彻底解决了这一问题。

Zod 类将其方法定义为标准 prototype 上的getter。当实例首次访问某个方法时,会通过标准原型继承传递至原型对象。该 getter 返回一个绑定到访问实例的方法实现,并将其作为自有属性 [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty] 赋值给该实例,从而永久实现记忆化。因此,后续对相同方法的访问将避免 getter 调用开销,直接通过自有属性解析。

这是对我之前描述的自覆盖 getter 模式的一种更精巧的变体。

从未被使用的实例方法(大部分方法均是如此)将永远不会被实例化,也不会作为自有属性存储在实例上。

Schema 类型4.4.3 版本4.5 版本缩减幅度
z.string()7.5kb784b9.8 倍
z.number()4.4kb706b6.4 倍
z.boolean()3.6kb594b6.3 倍
z.literal("a")5.1kb1.7kb3.0 倍
z.enum([...])5.3kb1.5kb3.5 倍
z.email()5.8kb2.2kb2.7 倍
z.uuid()5.9kb2.2kb2.6 倍
z.iso.datetime()6.1kb2.5kb2.4 倍
z.string().min(1)16.7kb3.4kb5.0 倍
z.string().min(1).max(5)25.8kb6.0kb4.3 倍
z.string().optional()12.6kb1.5kb8.4 倍
z.string().nullable()13.1kb1.5kb8.8 倍
z.string().default("")13.1kb1.9kb7.1 倍
z.string().brand()7.6kb872b8.9 倍
z.string().refine(...)20.5kb4.4kb4.7 倍
z.string().transform(...)17.5kb2.0kb8.5 倍
z.string().pipe(...)21.4kb2.2kb9.6 倍
z.array(z.string())11.2kb1.9kb5.8 倍
z.tuple([...]),3 个元素15.7kb2.8kb5.7 倍
z.record(...)16.4kb2.6kb6.2 倍
z.object({})6.6kb3.3kb2.0 倍
z.object({...}),3 个键22.2kb5.3kb4.2 倍
z.object({...}),10 个键82.0kb11.0kb7.5 倍
z.union([...]),2 个选项17.5kb2.1kb8.3 倍
z.discriminatedUnion(...),2 个选项42.0kb12.7kb3.3 倍
z.lazy(...)5.9kb1.3kb4.5 倍
zod/mini z.string()2.5kb577b4.4 倍
zod/mini z.object({...}),3 个键11.8kb3.2kb3.7 倍

旧方案的问题因 V8 引擎在对象自有属性内存分配上的一个细节而加剧:

  • 当自有属性少于 13 个时,V8 使用紧凑的 128 字节后备存储进行跟踪
  • 达到 13 个或更多时,后备存储扩大至 848 字节
  • 达到 21 个或更多时,再次增至 1616 字节

[email protected] 中,一个常规字符串 schema 拥有 49 个自有属性(40 个方法和 9 个属性),因此每个实例都会分配 1616 字节的后备存储。

而在新系统中,仅有六个属性是预绑定的:_zoddeftypeformatminLengthmaxLength。其余内容均存储于具有记忆化功能的原型上。

内存占用的测量可能存在一定复杂性。上述数据来源于仓库中的 packages/bench/memory/schema-footprint.ts(https://github.com/colinhacks/zod/blob/main/packages/bench/memory/schema-footprint.ts)。

升级至 Zod 4.5 以获取这些增强功能。

相似文章

优化1.1.1.1 DNS缓存以节省100TB内存

Hacker News Top

Cloudflare优化了其1.1.1.1 DNS缓存,将内存使用量减少超过50%,节省了100TB内存,并通过优化的Rust数据结构提升了性能。

Workers Cache

Hacker News Top

Cloudflare 推出 Workers Cache,这是一种位于 Workers 前面的分层缓存,允许在不调用 Worker 的情况下提供缓存响应,从而减少 CPU 时间并提高性能。