
import java.util.*;
import java.util.concurrent.*;

public class Garbage {
    // Hold a strong reference to the strings so that they're not collected when using the built in GC.
    private static String[] strings = new String[5000000];
    private static ConcurrentHashMap<String, String> stringsMap = new ConcurrentHashMap<String, String>(strings.length);
    private static ThreadFactory threadFactory = Executors.defaultThreadFactory();

    private static class TakeUpSomeSpace {
        private final String str;

        public TakeUpSomeSpace() {
            this.str = new String("str");
        }
    }

    public static void internViaBuiltin() {
        for (int i = 0; i < strings.length; ++i) {
            strings[i] = ("foo" + i).intern();
        }
    }

    public static String internViaMap(String str) {
        String interned = stringsMap.putIfAbsent(str, str);
        return interned == null ? str : interned;
    }

    // A (rough, stupid) version of how Guava interns
    public static void internViaMap() {
        for (int i = 0; i < strings.length; ++i) {
            strings[i] = internViaMap("foo" + i);
        }
    }

    public static void main(String[] args) {
        if (args.length < 1 || !Arrays.asList("builtin", "map").contains(args[0])) {
            System.err.println("You must specify 'builtin' or 'map'");
            System.exit(1);
        }

        // Intern a bunch of strings via either the builtin String.intern or a map.
        if ("builtin".equalsIgnoreCase(args[0])) {
            internViaBuiltin();
        } else {
            internViaMap();
        }

        long start = System.nanoTime();

        // Spam allocations so the GC has something to do.
        for (int i = 0; i < 100000; ++i) {
            allocate();
        }

        long end = System.nanoTime();

        System.out.printf("%.3f%n", (end - start) / 1e9);
    }

    private static void allocate() {
        final int count = 100000;
        List<TakeUpSomeSpace> spaces = new ArrayList<TakeUpSomeSpace>(count);
        for (int i = 0; i < count; ++i) {
            spaces.add(new TakeUpSomeSpace());
        }
    }
}
