Skip to content

Memory Optimization through Sharing

Share common state across huge numbers of objects

Builds on Strings and immutability and Design Pattern: Simple Factory (Creational)

Stage 3advanced3 min read

The Memory Exhaustion Problem

In this design, every object keeps all of its own data, even when most of that data is the same as in other objects. In large programs, such as a global map or a forest in a video game, this "Heavyweight" approach uses a huge amount of memory and can crash the program.


1. Real-Life Coding Example

Imagine you are building a feature like Google Maps that shows millions of trees across the globe. There are millions of trees, but most of them belong to only a few types like "Oak", "Pine", or "Birch".

Suppose we create a separate object for each tree, and each one stores its own tree type, color, and texture. This causes:

  • Redundant memory usage: Storing the string "Oak" and "Green" 1 million times.
  • Inefficient rendering: The graphics engine has to handle 1 million separate data sets instead of 1 shared one.
  • High GC Overhead: The Garbage Collector (the cleanup of unused objects) must track 1 million objects, which causes "stutters" (short pauses).

2. Key Issues in this Design

Lack of State Separation (Intrinsic vs. Extrinsic)

  • The Issue: The code puts Intrinsic state (data that never changes, like name, color, texture) and Extrinsic state (data that differs per tree, like x and y) in the same class.
  • The Impact: Because they are bundled together, we cannot share the constant data. Every time a new tree is planted, the whole data set is copied again.

Massive Memory Footprint

  • The Issue: 1 million Tree objects means 1 million copies of the same strings and metadata.
  • The Impact: The repo code assumes the type data of one tree takes about 1KB, so 1 million trees consume ~1GB of RAM. This number is illustrative, not measured. In a complex game with billions of objects, the program soon fails with OutOfMemoryError (the JVM has run out of memory).

Heavy Object Headers

  • The Issue: In languages like Java, every object has a header (extra bookkeeping data) of 12-16 bytes.
  • The Impact: For 1 million trees, you spend ~16MB just on headers, whatever the data inside. The bookkeeping costs more than the data it describes.

Slower Rendering & GC Pressure

  • The Issue: There are so many separate objects and references that the system slows down.
  • The Impact: Creating objects this fast keeps the Garbage Collector busy, so the program lags while it manages millions of small, repeated objects.

3. Structural Analysis

ComponentStatusObservation
Tree ObjectHeavyweightCarries redundant strings ("Oak", "Rough") in every instance.
Forest CollectionBloatedHolds 1 million unique references instead of a few shared types.
Client LogicInflexibleNo factory or cache mechanism to check if a "type" already exists.

4. Summary of the "Smell"

Watch for this: the program handles a huge number of objects, and most of their data is the same. That is a memory problem. The old code created a new Tree object for every request, so shared data was copied each time. The Flyweight Pattern fixes this by sharing the "intrinsic" state across all instances.

In this topic

Before code1 file

Before/Main.java81 linesGitHub
package DesignPatterns.StructuralPatterns.FlyweightDesignPattern.Before;import java.util.*;/** * PROBLEM: Redundant Data Storage. * Every 'Tree' object carries its own 'name', 'color', and 'texture'. * In a forest of 1 million trees, we store the string "Oak" 1 million times! */class Tree {    // Extrinsic State: Context-dependent (Changes for every tree)    private int x;    private int y;    // Intrinsic State: Context-independent (Constant for all trees of this type)    // ISSUE: These are duplicated in every single object instance.    private String name;    private String color;    private String texture;    public Tree(int x, int y, String name, String color, String texture) {        this.x = x;        this.y = y;        this.name = name;        this.color = color;        this.texture = texture;    }    public void draw() {        System.out.println(            "Drawing tree at (" + x + ", " + y + ") with type " + name        );    }}/** * PROBLEM: Inefficient Collection. * The 'Forest' is holding 1 million 'Tree' objects. * Each object has metadata overhead (headers, pointers) plus the redundant strings. */class Forest {    private List<Tree> trees = new ArrayList<>();    public void plantTree(        int x,        int y,        String name,        String color,        String texture    ) {        // ISSUE: 1,000,000 instances of 'Tree' are created.        // This will likely cause an OutOfMemoryError in a real high-fidelity simulation.        Tree tree = new Tree(x, y, name, color, texture);        trees.add(tree);    }    public void draw() {        for (Tree tree : trees) {            tree.draw();        }    }}class Main {    public static void main(String[] args) {        Forest forest = new Forest();        // SCENARIO: A game engine or a map simulation.        // If an 'Oak' type takes 1KB of memory, 1 million trees = 1GB of RAM.        // If we have 10 types of trees, we shouldn't be replicating type data.        for (int i = 0; i < 1000000; i++) {            forest.plantTree(i, i, "Oak", "Green", "Rough");        }        System.out.println("Planted 1 million trees.");    }}

Done with this topic?

How well do you know it?

PreviousThe Surrogate for Controlled Access