The Flyweight Pattern: Sharing Memory by Externalizing State

2026-05-08

You're rendering a forest with 1,000,000 trees. Each tree object holds a 2MB texture, a mesh, species metadata, and growth rules. Naively, that's 2TB of RAM. Your laptop is on fire. The Flyweight pattern is how you fix this without rewriting your renderer.

The core idea: separate the state that's shared across many objects (intrinsic state) from the state that's unique to each instance (extrinsic state). Store the intrinsic state once in a shared object — the flyweight — and pass extrinsic state in as method arguments at runtime.

For the forest: the texture, mesh, and species rules are intrinsic (a million oaks share one TreeType). The position, scale, and age are extrinsic — they live in the lightweight per-instance struct. Now you have ~10 TreeType flyweights and 1,000,000 tiny {x, y, z, scale, age, typeRef} records.

Real-world example: text editors. A document with 500,000 characters doesn't allocate 500,000 Character objects each holding font metrics, glyph bitmaps, and kerning tables. Instead, there's one Glyph flyweight per (character + font + size) combination — maybe 200 of them — and the document stores a stream of references plus position info. This is exactly how Java's String.intern() works, how browser font rendering works, and how game engines handle particle systems and tile maps.

How to spot a flyweight opportunity:

Rule of thumb: if intrinsic state is at least 10x larger than extrinsic state, and you have at least 1,000 instances, flyweight pays off. For the forest: intrinsic ≈ 2MB, extrinsic ≈ 32 bytes, instances = 1M. Memory savings ≈ (1M × 2MB) − (10 × 2MB + 1M × 32B) ≈ 2TB → 52MB. That's not optimization, that's the difference between shipping and not shipping.

The pitfalls: flyweights must be immutable — if one instance mutates shared state, every reference sees it. Always use a factory to hand out flyweights so you don't accidentally create duplicates that defeat the purpose. And don't reach for it prematurely; for 100 objects, the indirection costs more than it saves. Profile first, flyweight second.

Key Takeaway: When you have a sea of nearly-identical objects, split their state into shared (intrinsic) and per-instance (extrinsic), store the shared part once, and watch your memory footprint collapse.

All newsletters