Skip to main content

Waste Tab

The Waste tab identifies memory consumed by redundant or unnecessary objects — memory that can typically be recovered without changing application logic. This is complementary to leak detection: leaks are objects that should not exist; waste is objects that exist but are inefficiently stored.

What You See

Summary Bar

Four stat cards across the top:

┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Total Waste │ │ % of Heap │ │ Dup Strings │ │ Empty Colls │
│ 45.20 MB │ │ 8.8% │ │ 35.60 MB │ │ 9.60 MB │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
CardMeaning
Total WasteCombined bytes wasted by duplicate strings and empty collections
% of HeapWaste as a percentage of total heap size
Dup StringsBytes wasted by duplicate java.lang.String instances
Empty CollsBytes wasted by empty HashMap, ArrayList, LinkedHashMap instances

Duplicate Strings Table

Preview                                         Copies    Wasted
"application/json" 8,200 820 KB
"UTF-8" 6,100 390 KB
"true" 14,500 580 KB
"SELECT * FROM users WHERE id = ?" 2,300 230 KB
"" 45,000 1.80 MB
"https://api.example.com/v2/data" 1,800 198 KB
ColumnDescription
PreviewFirst 60 characters of the string content
CopiesHow many separate String instances have identical content
WastedBytes wasted: (copies - 1) × single_instance_size

Empty Collections Table

Class                              Count      Wasted
java.util.HashMap 120,000 5.70 MB
java.util.ArrayList 85,000 3.40 MB
java.util.LinkedHashMap 12,000 672 KB
ColumnDescription
ClassThe collection class
CountNumber of instances with size == 0
WastedTotal shallow size of all empty instances

How to Interpret the Results

High Duplicate String Waste

If duplicate strings account for more than 5% of the heap, consider:

  1. -XX:+UseStringDeduplication — Zero-code-change fix for G1 GC (JDK 8u20+). The GC identifies and deduplicates strings during collection.

  2. Application-level interning — For known high-frequency values:

    // Before: each deserialized JSON creates a new String
    String contentType = response.getHeader("Content-Type");

    // After: shared constant
    private static final String JSON_TYPE = "application/json";
    String contentType = response.getHeader("Content-Type");
    if (JSON_TYPE.equals(contentType)) contentType = JSON_TYPE;
  3. Enum replacement — If a string has a small set of known values ("ACTIVE", "INACTIVE", "PENDING"), replace with an enum.

Common Duplicate Strings

String PatternTypical SourceFix
"" (empty)Default values, optional fieldsUse null or a shared constant
"true" / "false"Serialization, config parsingParse to boolean
HTTP headersRepeated per requestConstant pool
SQL queriesORM-generated queriesStatement caching
Class names, package pathsReflection, loggingInterning

High Empty Collection Waste

120,000 empty HashMaps at 48-112 bytes each = 5.7 MB. This usually comes from:

  • Defensive allocation — Objects create new HashMap<>() in constructors even when the map is rarely populated
  • Deserialization — JSON/XML deserializers create empty collections for absent fields
  • Builder pattern — Builders that allocate all internal collections upfront

Fix: Lazy initialization:

// Before
class UserProfile {
Map<String, String> preferences = new HashMap<>();
List<Address> addresses = new ArrayList<>();
}

// After
class UserProfile {
Map<String, String> preferences; // null until first use
List<Address> addresses;

void addPreference(String key, String value) {
if (preferences == null) preferences = new HashMap<>(4);
preferences.put(key, value);
}
}

Example Walkthrough

Scenario: A REST API service uses 800 MB heap. The Overview tab shows no obvious leak. The Waste tab reveals:

Total Waste: 142 MB (17.7%)
├─ Duplicate Strings: 98 MB
└─ Empty Collections: 44 MB

Duplicate strings (top entries):

PreviewCopiesWasted
"Bearer "450,00018 MB
"application/json"380,00016 MB
"200"320,0008 MB

Diagnosis: The service handles 500K requests/minute and creates new String instances for HTTP header values per request. These strings are identical but not shared.

Fix: Add -XX:+UseStringDeduplication to JVM flags (immediate, no code changes). For a permanent fix, use a constant pool for known header values.

Empty collections:

ClassCountWasted
HashMap450,00021 MB
ArrayList380,00015 MB

Diagnosis: Each request DTO has Map<String, Object> metadata initialized eagerly but populated in only 5% of requests.

Fix: Change the field to null default with lazy initialization. Estimated savings: ~35 MB (most of the 450K empty HashMaps are from DTOs).

Total recoverable: ~142 MB (17.7% of heap) with two changes — JVM flag and lazy init.