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()); }
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; } }