Description / Meta Description
Learn the Flyweight Design Pattern in Java with practical examples. Understand how Flyweight reduces memory consumption by sharing common object state, improves scalability, and powers systems like Java String Pool, caching frameworks, gaming engines, and large-scale enterprise applications.
Flyweight Design Pattern in Java: Reducing Memory Usage by Sharing Common Object State
In the previous article, we explored the Bridge Pattern, which helps prevent class explosion by separating abstractions from implementations.
Now we arrive at the final Structural Design Pattern in the Gang of Four catalog:
Flyweight Pattern
Among all design patterns, Flyweight is one of the least frequently implemented directly by application developers.
However, it is one of the most heavily used patterns inside:
- JVM
- Java Collections
- Caching Systems
- Gaming Engines
- Enterprise Platforms
Flyweight solves a specific problem:
How do we efficiently manage millions of similar objects without exhausting memory?
The Problem: Memory Explosion
Imagine you’re building a document editor.
A document contains:
10,000 Pages
500,000 Words
5,000,000 Characters
Naive implementation:
Character c1 = new Character('A');
Character c2 = new Character('A');
Character c3 = new Character('A');
Character c4 = new Character('A');
Every character becomes a separate object.
Result:
Millions of Objects
Millions of Duplicates
High Memory Usage
Frequent GC Activity
Most of these objects contain identical data.
Huge waste.
Real World Example
Imagine a chess game.
A chess board contains:
White Pawn
White Pawn
White Pawn
White Pawn
White Pawn
White Pawn
White Pawn
White Pawn
Do we really need:
8 Separate White Pawn Objects
containing identical information?
Not necessarily.
Instead:
One Shared White Pawn Definition
can be reused across all positions.
This is the essence of Flyweight.
What is Flyweight Pattern?
Flyweight is a Structural Design Pattern that:
Minimizes memory usage by sharing common state among multiple objects.
Think of it like:
Company Logo
│
▼
Used by
100,000 Documents
Instead of storing the logo 100,000 times, store it once and share it.
Intrinsic vs Extrinsic State
Understanding Flyweight requires understanding two concepts.
Intrinsic State
Shared data.
Stored inside Flyweight.
Example:
Character = A
Font = Arial
Size = 12
These values rarely change.
Extrinsic State
Context-specific data.
Provided externally.
Example:
Position = Row 10
Position = Row 11
Position = Row 12
These values vary per usage.
Visual Representation
Shared Object (Flyweight)
Character: A
Font: Arial
Size: 12
│
├─────────► Position 1
├─────────► Position 2
├─────────► Position 3
└─────────► Position N
One object.
Many usages.
Flyweight Architecture
Client
│
▼
Flyweight Factory
│
▼
Shared Flyweight Objects
Factory ensures reuse.
Step 1: Create Flyweight Interface
public interface CharacterFlyweight {
void display(int position);
}
Step 2: Create Concrete Flyweight
public class CharacterA
implements CharacterFlyweight {
private final char value = 'A';
@Override
public void display(int position) {
System.out.println(
value + " at " + position);
}
}
Notice:
Character Value
is shared.
Position is external.
Step 3: Create Flyweight Factory
public class CharacterFactory {
private static Map<Character,
CharacterFlyweight> cache =
new HashMap<>();
public static CharacterFlyweight
getCharacter(char c) {
if(!cache.containsKey(c)) {
cache.put(c,
new CharacterA());
}
return cache.get(c);
}
}
Factory ensures reuse.
Step 4: Client Usage
CharacterFlyweight c1 =
CharacterFactory
.getCharacter('A');
CharacterFlyweight c2 =
CharacterFactory
.getCharacter('A');
Verification:
System.out.println(c1 == c2);
Output:
true
Same object reused.
Why Flyweight Works
Without Flyweight:
1 Million Characters
=
1 Million Objects
With Flyweight:
26 Letters
=
26 Shared Objects
Massive memory reduction.
Real JVM Example: String Pool
The most famous Flyweight implementation in Java.
Example:
String s1 = "hello";
String s2 = "hello";
Verification:
System.out.println(s1 == s2);
Output:
true
Why?
Because JVM maintains:
String Pool
Only one "hello" object exists.
Both references point to the same instance.
Visualizing String Pool
String Pool
"hello"
▲
│
┌─┴─┐
s1 s2
This is Flyweight in action.
Integer Cache Example
Another JVM optimization.
Example:
Integer a = 100;
Integer b = 100;
Output:
System.out.println(a == b);
true
Because JVM caches:
-128 to 127
Integer objects.
Shared instances reduce memory usage.
Gaming Industry Example
Imagine a game with:
10,000 Trees
Each tree contains:
Texture
Color
Shape
All identical.
Without Flyweight:
10,000 Texture Objects
Huge memory usage.
Flyweight:
One Shared Tree Definition
with:
Location
Rotation
Scale
provided externally.
Memory usage drops dramatically.
Enterprise Example: Product Catalog
Suppose an e-commerce platform contains:
1 Million Products
Many products share:
Brand
Category
Tax Rules
Shipping Rules
These can be stored once and shared.
Flyweight reduces memory pressure significantly.
Cache-Based Flyweight
Many enterprise applications implement Flyweight through caching.
Example:
Map<String, Currency>
Instead of:
new Currency("USD")
everywhere.
Use:
CurrencyFactory.get("USD");
Shared object.
Reusable memory.
Benefits of Flyweight Pattern
1. Reduced Memory Usage
Primary advantage.
2. Improved Scalability
Applications can handle significantly larger datasets.
3. Reduced Garbage Collection
Fewer objects.
Less GC pressure.
4. Better Performance
Lower memory allocation overhead.
5. Centralized Object Management
Factories control creation and reuse.
Common Mistakes
Mistake 1: Using Flyweight Prematurely
Not every application has memory issues.
Avoid unnecessary complexity.
Mistake 2: Sharing Mutable State
Shared objects should generally be immutable.
Bad:
sharedObject.setValue(...);
Multiple consumers may break.
Mistake 3: Ignoring Thread Safety
Shared objects often require careful concurrency handling.
Flyweight vs Singleton
Common interview question.
Singleton
Purpose:
One Object
Per Application
Example:
Logger
Configuration Manager
Flyweight
Purpose:
Many Shared Objects
Example:
Character Pool
String Pool
Currency Pool
Flyweight vs Prototype
Prototype
Creates copies.
Clone Existing Object
Flyweight
Avoids copies.
Reuse Existing Object
Structural Patterns Completed
| Pattern | Purpose |
|---|---|
| Adapter | Translate |
| Decorator | Enhance |
| Facade | Simplify |
| Proxy | Control |
| Composite | Organize |
| Bridge | Decouple |
| Flyweight | Share |
Structural Patterns are now complete.
Structural Pattern Cheat Sheet
Adapter
→ Make incompatible systems work together
Decorator
→ Add functionality dynamically
Facade
→ Hide complexity
Proxy
→ Control access
Composite
→ Represent hierarchies
Bridge
→ Separate abstraction from implementation
Flyweight
→ Share objects to save memory
Final Thoughts
The Flyweight Pattern solves a problem that becomes increasingly important at scale:
How do we manage millions of similar objects without running out of memory?
By separating shared state from contextual state and reusing objects wherever possible, Flyweight dramatically improves:
- Memory efficiency
- Scalability
- Performance
- JVM health
Even if you never implement Flyweight directly, you benefit from it every day through:
- String Pool
- Integer Cache
- JVM optimizations
- Caching frameworks
- Enterprise object registries
With this article, we’ve completed all 7 Structural Design Patterns.
In the next article, we’ll begin the largest and most practical GoF category:
Strategy Design Pattern — Selecting Algorithms Dynamically at Runtime
You’ll learn why Strategy powers payment processing, discount engines, pricing systems, sorting algorithms, Spring integrations, and countless enterprise applications.