private <T> Map<T, Long> snap(AtomicLongMap<T> atomic) {
   Map<T, Long> builder = new HashMap<T, Long>();
   for (Map.Entry<T, AtomicLong> e : atomic.underlying().entrySet()) {
     builder.put(e.getKey(), e.getValue().get());
   }
   return builder;
 }
  public AllocationSampler(Map<String, Long> trackSources, Set<String> trackLengths) {
    checkNotNull(trackSources, "trackSources");
    checkNotNull(trackLengths, "trackLengths");

    this.trackSources = trackSources;
    for (Map.Entry<String, Long> track : trackSources.entrySet())
      traces.put(track.getKey(), new ConcurrentLinkedQueue<StackTraceElement[]>());
    for (String name : trackLengths) arrayLengths.put(name, AtomicLongMap.<Integer>create());
  }
  @Override
  public void sampleAllocation(int count, String name, Object instance, long sizeBytes) {
    if (count != -1) {
      AtomicLongMap<Integer> lengths = arrayLengths.get(name);
      if (lengths != null) lengths.getAndIncrement(count);
    }

    Long threshold = trackSources.get(name);
    Long now = totalBytes.addAndGet(name, sizeBytes);

    if (threshold != null) {
      // if this whole lookup/check/put could be made atomic, that'd be awesome
      if (now - threshold >= lastSampledSize.get(name)) {
        lastSampledSize.put(name, now);
        // eek, object creation getting the trace
        StackTraceElement[] trace = AllocationEfficientStacktrace.stack(2, 50);
        traces.get(name).offer(trace);
      }
    }
  }
 // not atomic, so some information may be lost
 // and we may over- or under- sample
 public void clear() {
   totalBytes.clear();
   lastSampledSize.clear();
   for (Queue<?> el : traces.values()) el.clear();
   for (AtomicLongMap<?> el : arrayLengths.values()) el.clear();
 }
public class AllocationSampler implements Sampler {

  private final Map<String, Long> trackSources;
  private final AtomicLongMap<String> totalBytes = AtomicLongMap.create();

  private final AtomicLongMap<String> lastSampledSize = AtomicLongMap.create();
  private final Map<String, ConcurrentLinkedQueue<StackTraceElement[]>> traces =
      new ConcurrentHashMap<String, ConcurrentLinkedQueue<StackTraceElement[]>>();
  private final Map<String, AtomicLongMap<Integer>> arrayLengths =
      new ConcurrentHashMap<String, AtomicLongMap<Integer>>();

  public AllocationSampler(Map<String, Long> trackSources, Set<String> trackLengths) {
    checkNotNull(trackSources, "trackSources");
    checkNotNull(trackLengths, "trackLengths");

    this.trackSources = trackSources;
    for (Map.Entry<String, Long> track : trackSources.entrySet())
      traces.put(track.getKey(), new ConcurrentLinkedQueue<StackTraceElement[]>());
    for (String name : trackLengths) arrayLengths.put(name, AtomicLongMap.<Integer>create());
  }

  // not atomic, so some information may be lost
  // and we may over- or under- sample
  public void clear() {
    totalBytes.clear();
    lastSampledSize.clear();
    for (Queue<?> el : traces.values()) el.clear();
    for (AtomicLongMap<?> el : arrayLengths.values()) el.clear();
  }

  @Override
  public void sampleAllocation(int count, String name, Object instance, long sizeBytes) {
    if (count != -1) {
      AtomicLongMap<Integer> lengths = arrayLengths.get(name);
      if (lengths != null) lengths.getAndIncrement(count);
    }

    Long threshold = trackSources.get(name);
    Long now = totalBytes.addAndGet(name, sizeBytes);

    if (threshold != null) {
      // if this whole lookup/check/put could be made atomic, that'd be awesome
      if (now - threshold >= lastSampledSize.get(name)) {
        lastSampledSize.put(name, now);
        // eek, object creation getting the trace
        StackTraceElement[] trace = AllocationEfficientStacktrace.stack(2, 50);
        traces.get(name).offer(trace);
      }
    }
  }

  ////////////////////////////////////////////////////////
  // the following methods are not part of the sample path
  ////////////////////////////////////////////////////////

  public Map<String, Long> snapshotTotalBytes() {
    return snap(totalBytes);
  }

  public Map<String, List<StackTraceElement[]>> snapshotTraces() {
    Map<String, List<StackTraceElement[]>> snapshot =
        new HashMap<String, List<StackTraceElement[]>>();
    for (Map.Entry<String, ConcurrentLinkedQueue<StackTraceElement[]>> entry : traces.entrySet()) {
      if (entry.getValue().isEmpty()) continue;
      List<StackTraceElement[]> samples = new ArrayList<StackTraceElement[]>(entry.getValue());
      snapshot.put(entry.getKey(), samples);
    }
    return snapshot;
  }

  public Map<String, Map<Integer, Long>> snapshotArrayLengths() {
    Map<String, Map<Integer, Long>> snapshot = new HashMap<String, Map<Integer, Long>>();
    for (Map.Entry<String, AtomicLongMap<Integer>> entry : arrayLengths.entrySet()) {
      if (entry.getValue().isEmpty()) continue;
      Map<Integer, Long> lengths = snap(entry.getValue());
      snapshot.put(entry.getKey(), lengths);
    }
    return snapshot;
  }

  private <T> Map<T, Long> snap(AtomicLongMap<T> atomic) {
    Map<T, Long> builder = new HashMap<T, Long>();
    for (Map.Entry<T, AtomicLong> e : atomic.underlying().entrySet()) {
      builder.put(e.getKey(), e.getValue().get());
    }
    return builder;
  }
}