/* * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.visualvm.sampler.memory; import com.sun.tools.visualvm.application.jvm.HeapHistogram; import com.sun.tools.visualvm.application.jvm.Jvm; import com.sun.tools.visualvm.core.options.GlobalPreferences; import com.sun.tools.visualvm.core.ui.components.DataViewComponent; import com.sun.tools.visualvm.sampler.AbstractSamplerSupport; import com.sun.tools.visualvm.sampler.AbstractSamplerSupport.Refresher; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.lang.management.MemoryMXBean; import java.util.Set; import java.util.TimerTask; import javax.swing.SwingUtilities; import javax.swing.Timer; import org.netbeans.lib.profiler.common.ProfilingSettings; import org.netbeans.lib.profiler.results.memory.SampledMemoryResultsSnapshot; import org.openide.util.NbBundle; /** * * @author Jiri Sedlacek * @author Tomas Hurka */ public abstract class MemorySamplerSupport extends AbstractSamplerSupport { private final Jvm jvm; private final MemoryMXBean memoryBean; private final ThreadsMemory threadsMemory; private final HeapDumper heapDumper; private final SnapshotDumper snapshotDumper; private java.util.Timer processor; private Timer heapTimer; private Refresher heapRefresher; private MemoryView heapView; private final boolean hasPermGenHisto; private Timer permgenTimer; private Refresher permgenRefresher; private MemoryView permgenView; private Timer threadAllocTimer; private Refresher threadAllocRefresher; private ThreadsMemoryView threadAllocView; private DataViewComponent.DetailsView[] detailsViews; public MemorySamplerSupport(Jvm jvm, boolean hasPermGen, ThreadsMemory mem, MemoryMXBean memoryBean, SnapshotDumper snapshotDumper, HeapDumper heapDumper) { this.jvm = jvm; hasPermGenHisto = hasPermGen; threadsMemory = mem; this.memoryBean = memoryBean; this.heapDumper = heapDumper; this.snapshotDumper = snapshotDumper; } public DataViewComponent.DetailsView[] getDetailsView() { if (detailsViews == null) { initialize(); detailsViews = createViews(); } heapView.initSession(); if (permgenView != null) permgenView.initSession(); return detailsViews.clone(); } public boolean startSampling(ProfilingSettings settings, int samplingRate, int refreshRate) { // heapTimer.start(); // permgenTimer.start(); heapRefresher.setRefreshRate(samplingRate); if (permgenRefresher != null) permgenRefresher.setRefreshRate(samplingRate); if (threadAllocRefresher != null) threadAllocRefresher.setRefreshRate(samplingRate); if (heapView != null) { if (permgenView != null) doRefreshImpl(heapTimer, heapView, permgenView); else doRefreshImpl(heapTimer, heapView); } return true; } public synchronized void stopSampling() { heapTimer.stop(); if (permgenTimer != null) { permgenTimer.stop(); } if (threadAllocTimer != null) threadAllocTimer.stop(); if (heapView != null) { if (permgenView != null) doRefreshImplImpl(snapshotDumper.lastHistogram, heapView, permgenView); else doRefreshImplImpl(snapshotDumper.lastHistogram, heapView); } } public synchronized void terminate() { if (heapView != null) heapView.terminate(); if (permgenView != null) permgenView.terminate(); } private void initialize() { int defaultRefresh = GlobalPreferences.sharedInstance().getMonitoredDataPoll() * 1000; processor = getTimer(); heapTimer = new Timer(defaultRefresh, new ActionListener() { public void actionPerformed(ActionEvent e) { heapRefresher.refresh(); } }); heapRefresher = new Refresher() { public final boolean checkRefresh() { if (!heapTimer.isRunning()) return false; return (heapView.isShowing() || (permgenTimer != null && permgenTimer.getDelay() == heapTimer.getDelay() && permgenView.isShowing())); } public final void doRefresh() { if (heapView.isShowing()) { doRefreshImpl(heapTimer, heapView); } else if (permgenTimer != null && permgenTimer.getDelay() == heapTimer.getDelay() && permgenView.isShowing()) { doRefreshImpl(heapTimer, permgenView); } } public final void setRefreshRate(int refreshRate) { heapTimer.setDelay(refreshRate); heapTimer.setInitialDelay(refreshRate); heapTimer.restart(); } public final int getRefreshRate() { return heapTimer.getDelay(); } }; if (hasPermGenHisto) { permgenTimer = new Timer(defaultRefresh, new ActionListener() { public void actionPerformed(ActionEvent e) { permgenRefresher.refresh(); } }); permgenRefresher = new Refresher() { public final boolean checkRefresh() { if (!permgenTimer.isRunning()) return false; if (permgenTimer.getDelay() == heapTimer.getDelay()) return false; return (permgenView.isShowing()); } public final void doRefresh() { doRefreshImpl(permgenTimer, permgenView); } public final void setRefreshRate(int refreshRate) { permgenTimer.setDelay(refreshRate); permgenTimer.setInitialDelay(refreshRate); permgenTimer.restart(); } public final int getRefreshRate() { return permgenTimer.getDelay(); } }; } if (threadsMemory != null) { threadAllocTimer = new Timer(defaultRefresh, new ActionListener() { public void actionPerformed(ActionEvent e) { threadAllocRefresher.refresh(); } }); threadAllocRefresher = new Refresher() { public final boolean checkRefresh() { if (!threadAllocTimer.isRunning()) return false; return threadAllocView.isShowing(); } public final void doRefresh() { doRefreshImpl(threadAllocTimer, threadAllocView); } public final void setRefreshRate(int refreshRate) { threadAllocTimer.setDelay(refreshRate); threadAllocTimer.setInitialDelay(refreshRate); threadAllocTimer.restart(); } public final int getRefreshRate() { return threadAllocTimer.getDelay(); } }; } } private DataViewComponent.DetailsView[] createViews() { int detailIndex = 0; int detailsCount = 1; if (hasPermGenHisto) detailsCount++; if (threadAllocRefresher != null) detailsCount++; DataViewComponent.DetailsView[] details = new DataViewComponent.DetailsView[detailsCount]; heapView = new MemoryView(heapRefresher, MemoryView.MODE_HEAP, memoryBean, snapshotDumper, heapDumper); details[detailIndex++] = new DataViewComponent.DetailsView( NbBundle.getMessage(MemorySamplerSupport.class, "LBL_Heap_histogram"), // NOI18N null, 10, heapView, null); if (hasPermGenHisto) { permgenView = new MemoryView(permgenRefresher, MemoryView.MODE_PERMGEN, memoryBean, null, heapDumper); details[detailIndex++] = new DataViewComponent.DetailsView( NbBundle.getMessage(MemorySamplerSupport.class, "LBL_PermGen_histogram"), // NOI18N null, 20, permgenView, null); } if (threadAllocRefresher != null) { threadAllocView = new ThreadsMemoryView(threadAllocRefresher, memoryBean, heapDumper); details[detailIndex++] = new DataViewComponent.DetailsView( NbBundle.getMessage(MemorySamplerSupport.class, "LBL_ThreadAlloc"), // NOI18N null, 30, threadAllocView, null); } return details; } private void doRefreshImpl(final Timer timer, final ThreadsMemoryView view) { if (!timer.isRunning() || view.isPaused()) return; try { processor.schedule(new TimerTask() { public void run() { try { if (!timer.isRunning()) return; doRefreshImplImpl(threadsMemory.getThreadsMemoryInfo(), view); } catch (Exception e) { terminate(); } } }, 0); } catch (Exception e) { terminate(); } } private void doRefreshImpl(final Timer timer, final MemoryView... views) { if (!timer.isRunning() || (views.length == 1 && views[0].isPaused())) return; try { processor.schedule(new TimerTask() { public void run() { try { if (!timer.isRunning()) return; doRefreshImplImpl(jvm.takeHeapHistogram(), views); } catch (Exception e) { terminate(); } } }, 0); } catch (Exception e) { terminate(); } } private void doRefreshImplImpl(final HeapHistogram heapHistogram, final MemoryView... views) { if (heapHistogram != null) SwingUtilities.invokeLater(new Runnable() { public void run() { snapshotDumper.lastHistogram = heapHistogram; for (MemoryView view : views) view.refresh(heapHistogram); } }); } private void doRefreshImplImpl(final ThreadsMemoryInfo info, final ThreadsMemoryView view) { SwingUtilities.invokeLater(new Runnable() { public void run() { view.refresh(info); } }); } public static abstract class HeapDumper { public abstract void takeHeapDump(boolean openView); } public static abstract class SnapshotDumper { private volatile HeapHistogram lastHistogram; public abstract void takeSnapshot(boolean openView); public SampledMemoryResultsSnapshot createSnapshot(long time) { HeapHistogram histogram = lastHistogram; if (histogram != null) { ByteArrayOutputStream output = new ByteArrayOutputStream(1024); DataOutputStream dos = new DataOutputStream(output); try { SampledMemoryResultsSnapshot result = new SampledMemoryResultsSnapshot(); Set<HeapHistogram.ClassInfo> classes = histogram.getHeapHistogram(); dos.writeInt(1); // version dos.writeLong(histogram.getTime().getTime()); // begin time dos.writeLong(time); // taken time dos.writeInt(classes.size()); // no of classes for (HeapHistogram.ClassInfo info : classes) { dos.writeUTF(info.getName()); // name dos.writeLong(info.getBytes()); // total number of bytes } dos.writeBoolean(false); // no stacktraces dos.writeInt(classes.size()); // no of classes for (HeapHistogram.ClassInfo info : classes) { dos.writeInt((int)info.getInstancesCount()); // number of instances } dos.close(); result.readFromStream(new DataInputStream(new ByteArrayInputStream(output.toByteArray()))); return result; } catch (IOException ex) { ex.printStackTrace(); } } return null; } } }