
Zod 4.5 corta uso de memória de schemas em até 10x com memoização
O problema: métodos auto-bound estouram a memória
O Zod 4.5 introduz um padrão chamado "method memoization" que reduz drasticamente o consumo de memória dos schemas. Em versões anteriores, todos os métodos dos schemas eram automaticamente vinculados (auto-bound), ou seja, não dependiam da semântica implícita de this. Isso permite códigos como desestruturar parse diretamente de z.string() e chamá-lo isoladamente sem quebrar.
O custo dessa conveniência era que cada método se tornava uma closure vinculada ocupando espaço na memória, impedindo que múltiplas instâncias de uma classe compartilhassem métodos via herança de protótipo. O resultado concreto: em Zod 4.4, um simples z.string() retinha 7,5 kb de heap por instância; no 4.5, apenas 784 bytes - uma redução de 9,8x.
A solução: memoização via getters que se auto-substituem
A correção funciona assim: as classes do Zod definem seus métodos como getters em um protótipo padrão. Na primeira vez que um método é acessado, o getter retorna a implementação vinculada à instância e a atribui como own property nela, memoizando-a permanentemente. Acessos seguintes evitam o overhead do getter e resolvem direto pela own property.
O ponto-chave é que um método nunca usado (e a maioria não é) nunca é materializado nem armazenado na instância. O autor descreve o padrão como uma variante mais sofisticada de um "self-overwriting getter" que já havia descrito antes.
Como o V8 agrava o problema
Um detalhe da alocação de memória do V8 piorava o cenário antigo:
- Com menos de 13 own properties, o V8 usa um backing store compacto de 128 bytes;
- Com 13 ou mais, ele salta para 848 bytes;
- Com 21 ou mais, vai a 1.616 bytes.
Um schema de string no Zod 4.4.3 carregava 49 own properties (40 métodos e 9 propriedades), de modo que toda instância recebia o backing store de 1.616 bytes. No novo sistema, restam apenas seis propriedades eager: _zod, def, type, format, minLength e maxLength; todo o resto fica no protótipo memoizante (86 entradas no protótipo de z.string()).
Resultados medidos
Os ganhos variam conforme o tipo de schema: z.string() cai 9,8x (7,5 kb para 784 b), z.number() 6,4x, z.string().pipe(...) 9,6x, z.string().transform(...) 8,5x, z.string().optional() 8,4x e z.object({...}) com 10 chaves cai de 82 kb para 11 kb (7,5x). O menor ganho listado é z.object({}), com 2x. Medidas também cobrem zod/mini, como z.string() caindo de 2,5 kb para 577 b.
Os números vêm do benchmark packages/bench/memory/schema-footprint.ts do repositório, e o autor adverte que bytes retidos são difíceis de medir. A recomendação final é atualizar para o Zod 4.5 via npm upgrade zod@latest.