/*
* 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;
}
}
}