DEV Community

araf
araf

Posted on • Edited on

Java Memory Management Tips: Weak Maps, Weak Sets, and Pre-Sized Collections (with Examples)

When building Java apps that scale, efficient memory usage and performance become critical. Today, weโ€™ll look at two powerful (but often underused) tools:

  • ๐Ÿ”“ WeakHashMap / WeakHashSet โ€“ let the GC clean up what you donโ€™t need
  • ๐Ÿงฎ Predefining collection sizes โ€“ minimize resizing overhead

Letโ€™s break these down with examples.


๐Ÿ” WeakHashMap and WeakHashSet: Smart Memory-Friendly Containers

๐Ÿ”‘ WeakHashMap in Action

import java.util.Map;
import java.util.WeakHashMap;

public class WeakMapExample {
    public static void main(String[] args) {
        Map<Object, String> map = new WeakHashMap<>();
        Object key = new Object();

        map.put(key, "Some value");

        System.out.println("Before GC: " + map.size()); // 1

        key = null; // Remove strong reference
        System.gc(); // Hint the GC to collect

        try { Thread.sleep(100); } catch (InterruptedException ignored) {}

        System.out.println("After GC: " + map.size()); // Likely 0
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก The key is garbage collected because the map only holds a weak reference to it.


๐Ÿงบ Creating a WeakHashSet

import java.util.*;

public class WeakSetExample {
    public static void main(String[] args) {
        Set<Object> weakSet = Collections.newSetFromMap(new WeakHashMap<>());
        Object item = new Object();

        weakSet.add(item);
        System.out.println("Before GC: " + weakSet.size()); // 1

        item = null;
        System.gc();
        try { Thread.sleep(100); } catch (InterruptedException ignored) {}

        System.out.println("After GC: " + weakSet.size()); // Likely 0
    }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“Œ Use case: A cache of objects you donโ€™t want to prevent from being collected.


โš™๏ธ Predefining Collection Sizes for Performance

When you know (or can estimate) the number of elements, setting an initial capacity saves time and memory.

๐Ÿ†š Default vs Pre-Sized ArrayList

List<String> dynamicList = new ArrayList<>();
List<String> preSizedList = new ArrayList<>(1000);

for (int i = 0; i < 1000; i++) {
    dynamicList.add("Item " + i);
    preSizedList.add("Item " + i);
}
Enter fullscreen mode Exit fullscreen mode

โœ… The preSizedList avoids multiple internal array resizes as it grows, leading to:

  • Less heap churn
  • Fewer GC cycles
  • More predictable memory use

Predefining HashMap Size

Map<Integer, String> map = new HashMap<>(1000);
for (int i = 0; i < 1000; i++) {
    map.put(i, "Value " + i);
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Internally, every resize in HashMap involves rehashing all entries, which is expensive.


๐Ÿงช Benchmark It (Optional but Smart)

If you want to confirm performance gains in your context, try using JMH (Java Microbenchmark Harness). Itโ€™s a great way to test memory usage and execution time differences.


๐Ÿงต Recap

Concept Use It For Benefits Watch Out
WeakHashMap Caches, listeners, soft associations Auto-cleans unused keys Donโ€™t use for core state
WeakHashSet Temporary or optional references Prevents memory leaks Created via Collections.newSetFromMap()
Predefined Sizes Known/estimated data volume Faster, fewer allocations Overestimating wastes memory

๐Ÿ’ฌ Your Turn

Have you used weak references or optimized collection sizing in your projects? What kind of impact did you see? Share in the comments ๐Ÿ‘‡

Top comments (0)