/*
* Copyright 2008 to the original author or authors.
*
* 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.rioproject.impl.system.measurable.memory;
import org.rioproject.impl.system.measurable.MXBeanMonitor;
import org.rioproject.impl.system.measurable.SigarHelper;
import org.rioproject.system.MeasuredResource;
import org.rioproject.system.measurable.memory.ProcessMemoryUtilization;
import org.rioproject.watch.ThresholdValues;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.text.NumberFormat;
/**
* The <code>ProcessMemoryMonitor</code> object provides feedback information to the
* <code>Memory</code> object, providing memory usage information for a process
* obtained using JMX and SIGAR. SIGAR is used to obtain the process shared,
* virtual and real memory size.
*
* <p>JMX is used to obtain detailed information on
* heap and non-heap usage. If SIGAR is not available, only JMX will be used.
*
* @author Dennis Reedy
*/
public class ProcessMemoryMonitor implements MXBeanMonitor<MemoryMXBean> {
private MemoryMXBean memBean;
private String id;
private ThresholdValues tVals;
private SigarHelper sigar;
private long pid;
static Logger logger = LoggerFactory.getLogger(ProcessMemoryMonitor.class);
private static double KB = 1024;
private static double MB = Math.pow(KB, 2);
public ProcessMemoryMonitor() {
sigar = SigarHelper.getInstance();
if (sigar!=null) {
pid = sigar.getPid();
}
memBean = ManagementFactory.getMemoryMXBean();
}
public ProcessMemoryMonitor(int pid) {
sigar = SigarHelper.getInstance();
if (sigar!=null) {
this.pid = pid;
}
}
public void setPID(long pid) {
this.pid = pid;
}
/* (non-Javadoc)
* @see org.rioproject.system.measurable.MeasurableMonitor#terminate()
*/
public void terminate() {
}
public void setID(String id) {
this.id = id;
}
public void setThresholdValues(ThresholdValues tVals) {
this.tVals = tVals;
}
public MeasuredResource getLastMeasuredResource() {
return getMeasuredResource();
}
public MeasuredResource getMeasuredResource() {
//if (memBean == null)
// memBean = ManagementFactory.getMemoryMXBean();
ProcessMemoryUtilization memoryUtilization;
MemoryUsage heapUsage = null;
MemoryUsage nonHeapUsage = null;
double utilization = 0;
if(memBean!=null) {
heapUsage = memBean.getHeapMemoryUsage();
nonHeapUsage = memBean.getNonHeapMemoryUsage();
utilization = (double)heapUsage.getUsed()/(double)heapUsage.getMax();
}
if (sigar!=null) {
try {
double vSize = sigar.getProcessVirtualMemorySize(pid)/MB;
double resident = sigar.getProcessResidentMemory(pid);
double shared = sigar.getProcessSharedMemory(pid);
vSize = (vSize>0?vSize/MB:vSize);
resident = (resident>0?resident/MB:resident);
shared = (shared>0?shared/MB:shared);
if(logger.isTraceEnabled()) {
NumberFormat nf = NumberFormat.getInstance();
StringBuilder builder = new StringBuilder();
nf.setMaximumFractionDigits(2);
builder.append("\n");
builder.append("VSize = ").append(nf.format(vSize)).append(" MB\n");
builder.append("Resident = ").append(nf.format(resident)).append(" MB\n");
builder.append("Shared = ").append(shared).append("\n");
builder.append("Heap Init = ").append(nf.format(heapUsage.getInit()/MB)).append(" MB\n");
builder.append("Heap Used = ").append(nf.format(heapUsage.getUsed()/MB)).append(" MB\n");
builder.append("Heap Max = ").append(nf.format(heapUsage.getMax()/MB)).append(" MB\n");
builder.append("Heap Committed = ").append(nf.format(heapUsage.getMax()/MB)).append(" MB\n");
builder.append("Non Heap Init = ").append(nf.format(nonHeapUsage.getInit()/MB)).append(" MB\n");
builder.append("Non Heap Used = ").append(nf.format(nonHeapUsage.getUsed()/MB)).append(" MB\n");
builder.append("Non Heap Max = ").append(nf.format(nonHeapUsage.getMax()/MB)).append(" MB\n");
builder.append("Non Heap Committed = ").append(nf.format(nonHeapUsage.getMax()/MB)).append(" MB");
logger.trace(builder.toString());
}
if(heapUsage!=null) {
memoryUtilization = new ProcessMemoryUtilization(id,
utilization,
vSize,
resident,
shared,
heapUsage.getInit()/MB,
heapUsage.getUsed()/MB,
heapUsage.getMax()/MB,
heapUsage.getCommitted()/MB,
nonHeapUsage.getInit()/MB,
nonHeapUsage.getUsed()/MB,
nonHeapUsage.getMax()/MB,
nonHeapUsage.getCommitted()/MB,
tVals);
} else {
memoryUtilization = new ProcessMemoryUtilization(id, utilization, vSize, resident, shared, tVals);
}
} catch (Exception e) {
logger.warn("SIGAR exception getting Process Memory", e);
memoryUtilization = getJvmMemoryUtilization(utilization, heapUsage, nonHeapUsage);
}
} else {
memoryUtilization = getJvmMemoryUtilization(utilization, heapUsage, nonHeapUsage);
}
return memoryUtilization;
}
public void setMXBean(MemoryMXBean mxBean) {
this.memBean = mxBean;
}
public MemoryMXBean getMXBean() {
return memBean;
}
private ProcessMemoryUtilization getJvmMemoryUtilization(double utilization,
MemoryUsage heapUsage,
MemoryUsage nonHeapUsage) {
return new ProcessMemoryUtilization(id,
utilization,
getInit(heapUsage),
getUsed(heapUsage),
getMax(heapUsage),
getCommitted(heapUsage),
getInit(nonHeapUsage),
getUsed(nonHeapUsage),
getMax(nonHeapUsage),
getCommitted(nonHeapUsage),
tVals);
}
private double getInit(MemoryUsage mUsage) {
return mUsage==null?-1:mUsage.getInit()/MB;
}
private double getUsed(MemoryUsage mUsage) {
return mUsage==null?-1:mUsage.getUsed()/MB;
}
private double getMax(MemoryUsage mUsage) {
return mUsage==null?-1:mUsage.getMax()/MB;
}
private double getCommitted(MemoryUsage mUsage) {
return mUsage==null?-1:mUsage.getCommitted()/MB;
}
}