Flyweight Design Pattern in Java: Reducing Memory Usage by Sharing Common Object State

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

PatternPurpose
AdapterTranslate
DecoratorEnhance
FacadeSimplify
ProxyControl
CompositeOrganize
BridgeDecouple
FlyweightShare

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.

Leave a Reply

Your email address will not be published. Required fields are marked *