MemoryUsageSampler.java
/*
* Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Additionally licensed with:
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spf4j.perf.memory;
//CHECKSTYLE:OFF
import com.sun.management.HotSpotDiagnosticMXBean;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
//CHECKSTYLE:ON
import java.io.IOException;
import org.spf4j.base.AbstractRunnable;
import org.spf4j.concurrent.DefaultScheduler;
import org.spf4j.perf.impl.RecorderFactory;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import org.spf4j.jmx.JmxExport;
import org.spf4j.jmx.Registry;
import org.spf4j.perf.CloseableMeasurementRecorder;
/**
* This class allows you to poll and recordAt to a file the heap committed and heap used for your java process. start
* data recording by calling the startMemoryUsageSampling method, stop the data recording by calling the method:
* startMemoryUsageSampling.
*
* @author zoly
*/
@SuppressFBWarnings("IICU_INCORRECT_INTERNAL_CLASS_USE")
public final class MemoryUsageSampler {
private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";
private static final MemoryMXBean MBEAN = ManagementFactory.getMemoryMXBean();
private static final HotSpotDiagnosticMXBean HOTSPOT_DIAGNOSTIC_INSTANCE = getHotspotMBean();
private static ScheduledFuture<?> samplingFuture;
private static AccumulatorRunnable accumulatorRunnable;
static {
org.spf4j.base.Runtime.queueHook(2, new AbstractRunnable(true) {
@Override
public void doRun() {
stop();
}
});
Registry.export(MemoryUsageSampler.class);
}
private static HotSpotDiagnosticMXBean getHotspotMBean() {
try {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
return ManagementFactory.newPlatformMXBeanProxy(server,
HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class);
} catch (IOException ex) {
throw new ExceptionInInitializerError(ex);
}
}
private MemoryUsageSampler() {
}
public static synchronized void start(final long sampleTimeMilis) {
start((int) sampleTimeMilis, (int) sampleTimeMilis * 10);
}
public static synchronized void start(final int sampleTimeMilis) {
start(sampleTimeMilis, sampleTimeMilis * 10);
}
@JmxExport
public static synchronized void start(@JmxExport("sampleTimeMillis") final int sampleTimeMilis,
@JmxExport("accumulateIntervalMillis") final int accumulateIntervalMillis) {
if (samplingFuture == null) {
accumulatorRunnable = new AccumulatorRunnable(accumulateIntervalMillis);
samplingFuture = DefaultScheduler.INSTANCE.scheduleWithFixedDelay(accumulatorRunnable,
sampleTimeMilis, sampleTimeMilis, TimeUnit.MILLISECONDS);
} else {
throw new IllegalStateException("Memory usage sampling already started " + samplingFuture);
}
}
@JmxExport
public static synchronized void stop() {
if (samplingFuture != null) {
samplingFuture.cancel(false);
accumulatorRunnable.close();
samplingFuture = null;
}
}
@JmxExport
public static synchronized boolean isStarted() {
return samplingFuture != null;
}
private static class AccumulatorRunnable extends AbstractRunnable implements AutoCloseable {
private final CloseableMeasurementRecorder heapCommited;
private final CloseableMeasurementRecorder heapUsed;
private final CloseableMeasurementRecorder nonHeapCommited;
private final CloseableMeasurementRecorder nonHeapUsed;
AccumulatorRunnable(final int accumulationIntervalMillis) {
heapCommited = RecorderFactory.createScalableMinMaxAvgRecorder2("process.heap_commited",
"bytes", accumulationIntervalMillis);
heapUsed = RecorderFactory.createScalableMinMaxAvgRecorder2("process.heap_used",
"bytes", accumulationIntervalMillis);
nonHeapCommited = RecorderFactory.createScalableMinMaxAvgRecorder2("process.non_heap_commited",
"bytes", accumulationIntervalMillis);
nonHeapUsed = RecorderFactory.createScalableMinMaxAvgRecorder2("process.non_heap_used",
"bytes", accumulationIntervalMillis);
}
@Override
public void doRun() throws Exception {
MemoryUsage usage = MBEAN.getHeapMemoryUsage();
heapCommited.record(usage.getCommitted());
heapUsed.record(usage.getUsed());
MemoryUsage nonHeapMemoryUsage = MBEAN.getNonHeapMemoryUsage();
nonHeapCommited.record(nonHeapMemoryUsage.getCommitted());
nonHeapUsed.record(nonHeapMemoryUsage.getUsed());
}
@Override
public void close() {
heapUsed.close();
heapCommited.close();
nonHeapCommited.close();
nonHeapUsed.close();
}
}
public static void dumpHeap(final String filename, final boolean liveObjectOnly) throws IOException {
HOTSPOT_DIAGNOSTIC_INSTANCE.dumpHeap(filename, liveObjectOnly);
}
@SuppressFBWarnings("MS_EXPOSE_REP")
public static HotSpotDiagnosticMXBean getHotspotDiagnosticBean() {
return HOTSPOT_DIAGNOSTIC_INSTANCE;
}
}