The Flyweight Pattern is a structural Gang of Four pattern that saves RAM by sharing the parts of objects that do not change. Rather than allocating a full copy of repeated data for every object, an application keeps one immutable shared object and supplies each use with its changing context. For programs that create enormous numbers of fine-grained objects, that small shift matters.
Separate shared data from per-use data
At the core of the design, split an object’s state in two. Intrinsic state is constant, independent of context, and safe to share. Within the flyweight, it must be read-only to clients. Extrinsic state, by contrast, varies by use and is supplied when the flyweight performs an operation.
| State type | Where it lives | Particle example |
|---|---|---|
| Intrinsic | Inside the shared flyweight | Color and sprite for a particle type |
| Extrinsic | With the client or passed to an operation | Coordinates, movement vector, and speed |
When coordinates sit in a shared particle object, one particle’s movement becomes visible to every user of that object. With those coordinates outside it, a single immutable particle type can serve many independent particles.
See why sharing changes the memory equation
In a document, glyphs make the pattern tangible. Despite many occurrences of the letter A, a document does not need a separate object for its font data, outline, and metrics at every occurrence. For those shared properties, one glyph object is enough while document layout keeps each character’s position separately. At hundreds or thousands of bytes per character for a naive glyph object, sharing avoids duplicating a substantial immutable payload.
At a larger scale, the same shape appears in a game engine. In a forest containing 1,000,000 trees, a TreeType can retain a tree’s name, color, and texture. Per instance, only x/y coordinates and a reference to that type are needed in each Tree. Similarly, a particle system that once created complete objects for every bullet, rocket, and shrapnel fragment can reuse three particle types thousands of times while receiving position data externally.
Use a factory to make sharing invisible to clients
In a typical implementation, four participants cooperate: a flyweight interface that accepts extrinsic parameters, concrete flyweights holding intrinsic data, a factory that creates and caches them, and a client that requests them. For a key request, the factory either returns the existing object or creates, stores, and returns one. To the caller, the two cases look the same.
final class TreeType {
private final String name;
private final String color;
private final String texture;
TreeType(String name, String color, String texture) {
this.name = name;
this.color = color;
this.texture = texture;
}
}
final class TreeTypeFactory {
private final Map<String, TreeType> cache = new HashMap<>();
TreeType get(String name, String color, String texture) {
String key = name + "|" + color + "|" + texture;
return cache.computeIfAbsent(key,
ignored -> new TreeType(name, color, texture));
}
}
To distinguish shared objects correctly, the cache key must cover every intrinsic property. In this example, a request for the same name, color, and texture receives the same TreeType; a different texture requires a different one. Although a factory is often implemented as a Singleton to provide global access, the essential behavior is caching and reuse, not the access mechanism.
Keep the flyweight immutable and pass context at the call
For a flyweight, immutability is the contract that makes sharing safe. After receiving an object, the client must not mutate its intrinsic state. Instead, methods accept the information that differs for this particular use:
interface ParticleType {
void draw(int x, int y, int speed);
}
At each call, the particle type can retain its color and sprite while x, y, and speed describe one rendering event. By keeping those values out of the object, the design prevents accidental sharing of changing state. In concurrent code, shared objects also require deliberate thread-safety design; mutable shared state invites race conditions.
Recognize flyweights already present in Java
Within the Java standard library, Integer.valueOf(int) is a direct example. From -128 through 127, cached Integer objects let repeated requests return an existing object rather than allocate another wrapper. The VM property java.lang.Integer.IntegerCache.high can raise the cache limit.
For equal string values, string interning applies the same sharing idea. Because strings are immutable, a string pool can store one copy of each distinct value. At compile time, Java automatically interns string constants, and String.intern() can use the pool explicitly. When equal values refer to the same pooled object, a pointer comparison is O(1) rather than a character-by-character O(n) comparison.
Elsewhere in the library, classes including Boolean, Byte, Character, Short, Long, and BigDecimal expose comparable valueOf() caching behavior. When browsing unfamiliar source code, a cached-object creation method such as valueOf() or getFlyweight() is a useful clue.
Choose Flyweight only after measuring a memory problem
As a memory optimization, Flyweight is not a default way to model every object. Its complexity is justified when an application creates a very large number of small objects, the total memory cost is prohibitive, and those objects have substantial shared data with only a few variations. Examples fitting that shape include text glyphs, map elements, reusable GUI components, and repeated game assets.
Before changing the design, start with a measurement and then inspect the object state. The following sequence keeps the refactoring focused:
- Confirm that RAM consumption is a demonstrated problem.
- Find the data repeated across many instances.
- Move only invariant, context-independent data into an immutable flyweight.
- Keep changing values with the client and pass them to operations.
- Use a factory cache keyed by the intrinsic state.
- Review concurrent access to the shared objects and factory.
Without a RAM constraint, the extra separation, cache management, and concurrency reasoning can cost more in complexity than it returns in savings.
Avoid the mistakes that turn sharing into coupling
Myth: a flyweight is simply any cache. Although a factory cache is part of the common implementation, the pattern’s defining move is separating shareable intrinsic state from changing extrinsic state.
Myth: all state can be kept in the shared object. By storing extrinsic state in the flyweight, a design removes the independence that made sharing possible and can create race conditions. Outside the shared object belong coordinates, movement, and other per-use values.
Myth: the pattern always makes an application faster. Reducing memory use is its stated purpose. While string interning can make some equality checks cheaper, Flyweight itself should be selected because measurement shows duplicated object data is a memory cost.
Frequently Asked Questions
What is the Flyweight Pattern?
As a structural GoF pattern, it reduces RAM use by sharing immutable, common object data and supplying changing context separately.
What is the difference between intrinsic and extrinsic state?
Because intrinsic state is constant and context-independent, the flyweight stores it. From one use to another, extrinsic state changes and is passed to the flyweight operation or kept by the client.
What does a flyweight factory do?
On the first request for an intrinsic-state key, it creates and caches a flyweight; for later matching requests, it returns the cached object.
Is Integer.valueOf() an example of Flyweight?
Yes. In the range from -128 to 127, Java caches Integer objects, allowing repeated requests to reuse an object.
When should a developer use Flyweight?
After measurement shows that a huge population of fine-grained objects consumes too much RAM, use it when many objects share substantial invariant data.

