/* * Copyright (c) 2007, 2011, 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.application.views.monitor; import com.sun.tools.visualvm.application.Application; import com.sun.tools.visualvm.application.jvm.Jvm; import com.sun.tools.visualvm.application.jvm.JvmFactory; import com.sun.tools.visualvm.application.jvm.MonitoredData; import com.sun.tools.visualvm.application.jvm.MonitoredDataListener; import com.sun.tools.visualvm.charts.SimpleXYChartSupport; import com.sun.tools.visualvm.core.datasource.DataSource; import com.sun.tools.visualvm.core.datasource.Storage; import com.sun.tools.visualvm.core.options.GlobalPreferences; import com.sun.tools.visualvm.core.snapshot.Snapshot; import com.sun.tools.visualvm.heapdump.HeapDumpSupport; import com.sun.tools.visualvm.tools.jmx.JmxModel; import com.sun.tools.visualvm.tools.jmx.JmxModel.ConnectionState; import com.sun.tools.visualvm.tools.jmx.JmxModelFactory; import com.sun.tools.visualvm.tools.jmx.JvmMXBeans; import com.sun.tools.visualvm.tools.jmx.JvmMXBeansFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openide.util.NbBundle; import org.openide.util.RequestProcessor; /** * * @author Jiri Sedlacek */ final class ApplicationMonitorModel { private static final String PROP_PREFIX = "ApplicationMonitorModel_"; // NOI18N static final String SNAPSHOT_VERSION = PROP_PREFIX + "version"; // NOI18N private static final String SNAPSHOT_VERSION_DIVIDER = "."; // NOI18N private static final String CURRENT_SNAPSHOT_VERSION_MAJOR = "1"; // NOI18N private static final String CURRENT_SNAPSHOT_VERSION_MINOR = "1"; // NOI18N private static final String CURRENT_SNAPSHOT_VERSION = CURRENT_SNAPSHOT_VERSION_MAJOR + SNAPSHOT_VERSION_DIVIDER + CURRENT_SNAPSHOT_VERSION_MINOR; private static final String PROP_NOT_DEFINED = "<not defined>"; // NOI18N public static final String PROP_CHART_CACHE = PROP_PREFIX + "chart_cache"; // NOI18N public static final String PROP_UPTIME = PROP_PREFIX + "uptime"; // NOI18N public static final String PROP_PREV_UPTIME = PROP_PREFIX + "prev_uptime"; // NOI18N public static final String PROP_INVOKE_GC_SUPPORTED = PROP_PREFIX + "invoke_gc_supported"; // NOI18N public static final String PROP_HEAP_DUMP_SUPPORTED = PROP_PREFIX + "heap_dump_supported"; // NOI18N public static final String PROP_CPU_MONITORING_SUPPORTED = PROP_PREFIX + "cpu_monitoring_supported"; // NOI18N public static final String PROP_GC_MONITORING_SUPPORTED = PROP_PREFIX + "gc_monitoring_supported"; // NOI18N public static final String PROP_MEMORY_MONITORING_SUPPORTED = PROP_PREFIX + "memory_monitoring_supported"; // NOI18N public static final String PROP_CLASS_MONITORING_SUPPORTED = PROP_PREFIX + "class_monitoring_supported"; // NOI18N public static final String PROP_THREADS_MONITORING_SUPPORTED = PROP_PREFIX + "threads_monitoring_supported"; // NOI18N public static final String PROP_NUMBER_OF_PROCESSORS = PROP_PREFIX + "number_of_processors"; // NOI18N public static final String PROP_PROCESS_CPU_TIME = PROP_PREFIX + "process_cpu_time"; // NOI18N public static final String PROP_PROCESS_GC_TIME = PROP_PREFIX + "process_gc_time"; // NOI18N public static final String PROP_PREV_PROCESS_CPU_TIME = PROP_PREFIX + "prev_process_cpu_time"; // NOI18N public static final String PROP_PREV_PROCESS_GC_TIME = PROP_PREFIX + "prev_process_gc_time"; // NOI18N public static final String PROP_HEAP_NAME = PROP_PREFIX + "heap_name"; // NOI18N public static final String PROP_HEAP_CAPACITY = PROP_PREFIX + "heap_capacity"; // NOI18N public static final String PROP_HEAP_USED = PROP_PREFIX + "heap_used"; // NOI18N public static final String PROP_MAX_HEAP = PROP_PREFIX + "max_heap"; // NOI18N public static final String PROP_PERMGEN_NAME = PROP_PREFIX + "permgen_name"; // NOI18N public static final String PROP_PERMGEN_CAPACITY = PROP_PREFIX + "permgen_capacity"; // NOI18N public static final String PROP_PERMGEN_USED = PROP_PREFIX + "permgen_used"; // NOI18N public static final String PROP_PERMGEN_MAX = PROP_PREFIX + "permgen_max"; // NOI18N public static final String PROP_SHARED_UNLOADED = PROP_PREFIX + "shared_unloaded"; // NOI18N public static final String PROP_TOTAL_UNLOADED = PROP_PREFIX + "total_unloaded"; // NOI18N public static final String PROP_SHARED_LOADED = PROP_PREFIX + "shared_loaded"; // NOI18N public static final String PROP_TOTAL_LOADED = PROP_PREFIX + "total_loaded"; // NOI18N public static final String PROP_TOTAL_THREADS = PROP_PREFIX + "total_threads"; // NOI18N public static final String PROP_DAEMON_THREADS = PROP_PREFIX + "daemon_threads"; // NOI18N public static final String PROP_PEAK_THREADS = PROP_PREFIX + "peak_threads"; // NOI18N public static final String PROP_STARTED_THREADS = PROP_PREFIX + "started_threads"; // NOI18N private static final String CPU_CHART_STORAGE = "monitor_cpu.dat"; // NOI18N private static final String HEAP_CHART_STORAGE = "monitor_heap.dat"; // NOI18N private static final String PERMGEN_CHART_STORAGE = "monitor_permgen.dat"; // NOI18N private static final String CLASSES_CHART_STORAGE = "monitor_classes.dat"; // NOI18N private static final String THREADS_CHART_STORAGE = "monitor_threads.dat"; // NOI18N private boolean initialized; private final DataSource source; private final boolean live; private final List<ChangeListener> listeners; private Jvm jvm; private MemoryMXBean memoryMXBean; private MonitoredDataListener monitoredDataListener; private int chartCache = -1; private long timestamp = -1; private long uptime = -1; private long prevUpTime = -1; private boolean takeHeapDumpSupported = false; private boolean cpuMonitoringSupported = false; private boolean gcMonitoringSupported = false; private boolean memoryMonitoringSupported = false; private boolean classMonitoringSupported = false; private boolean threadsMonitoringSupported = false; private int processorsCount = -1; private long processCpuTime = -1; private long processGcTime = -1; private long prevProcessCpuTime = -1; private long prevProcessGcTime = -1; private String heapName; private long heapCapacity = -1; private long heapUsed = -1; private long maxHeap = -1; private String permgenName; private long permgenCapacity = -1; private long permgenUsed = -1; private long permgenMax = -1; private long sharedUnloaded = -1; private long totalUnloaded = -1; private long sharedLoaded = -1; private long totalLoaded = -1; private long totalThreads = -1; private long daemonThreads = -1; private long peakThreads = -1; private long startedThreads = -1; private SimpleXYChartSupport cpuChartSupport; private SimpleXYChartSupport heapChartSupport; private SimpleXYChartSupport permGenChartSupport; private SimpleXYChartSupport classesChartSupport; private SimpleXYChartSupport threadsChartSupport; public static ApplicationMonitorModel create(Application application, boolean live) { return new ApplicationMonitorModel(application, live); } public static ApplicationMonitorModel create(Snapshot snapshot) { return new ApplicationMonitorModel(snapshot, false); } public DataSource getSource() { return source; } public boolean isLive() { return live; } public MemoryMXBean getMemoryMXBean() { return memoryMXBean; } public int getChartCache() { return chartCache; } public boolean isInvokeGCSupported() { return memoryMXBean != null; } public boolean isTakeHeapDumpSupported() { return takeHeapDumpSupported; } public boolean isCpuMonitoringSupported() { return cpuMonitoringSupported; } public boolean isGcMonitoringSupported() { return gcMonitoringSupported; } public boolean isMemoryMonitoringSupported() { return memoryMonitoringSupported; } public boolean isClassMonitoringSupported() { return classMonitoringSupported; } public boolean isThreadsMonitoringSupported() { return threadsMonitoringSupported; } public int getProcessorsCount() { return processorsCount; } public long getTimestamp() { return timestamp; } public long getUpTime() { return uptime; } public long getPrevUpTime() { return prevUpTime; } public long getProcessCpuTime() { return processCpuTime; } public long getProcessGcTime() { return processGcTime; } public long getPrevProcessCpuTime() { return prevProcessCpuTime; } public long getPrevProcessGcTime() { return prevProcessGcTime; } public String getHeapName() { return heapName; } public long getHeapCapacity() { return heapCapacity; } public long getHeapUsed() { return heapUsed; } public long getMaxHeap() { return maxHeap; } public String getPermgenName() { return permgenName; } public long getPermgenCapacity() { return permgenCapacity; } public long getPermgenUsed() { return permgenUsed; } public long getPermgenMax() { return permgenMax; } public long getSharedUnloaded() { return sharedUnloaded; } public long getTotalUnloaded() { return totalUnloaded; } public long getSharedLoaded() { return sharedLoaded; } public long getTotalLoaded() { return totalLoaded; } public long getTotalThreads() { return totalThreads; } public long getDeamonThreads() { return daemonThreads; } public long getPeakThreads() { return peakThreads; } public long getStartedThreads() { return startedThreads; } public synchronized void initialize() { if (initialized) return; initialized = true; if (source instanceof Application) initialize((Application)source); else initialize((Snapshot)source); } public void registerCpuChartSupport(final SimpleXYChartSupport cpuChartSupport) { this.cpuChartSupport = cpuChartSupport; if (cpuChartSupport != null && source instanceof Snapshot) RequestProcessor.getDefault().post(new Runnable() { public void run() { File file = new File(source.getStorage().getDirectory(), CPU_CHART_STORAGE); if (file.isFile()) loadChartSupport(cpuChartSupport, file); } }); } public void registerHeapChartSupport(final SimpleXYChartSupport heapChartSupport) { this.heapChartSupport = heapChartSupport; if (heapChartSupport != null && source instanceof Snapshot) RequestProcessor.getDefault().post(new Runnable() { public void run() { File file = new File(source.getStorage().getDirectory(), HEAP_CHART_STORAGE); if (file.isFile()) loadChartSupport(heapChartSupport, file); } }); } public void registerPermGenChartSupport(final SimpleXYChartSupport permgenChartSupport) { this.permGenChartSupport = permgenChartSupport; if (permGenChartSupport != null && source instanceof Snapshot) RequestProcessor.getDefault().post(new Runnable() { public void run() { File file = new File(source.getStorage().getDirectory(), PERMGEN_CHART_STORAGE); if (file.isFile()) loadChartSupport(permGenChartSupport, file); } }); } public void registerClassesChartSupport(final SimpleXYChartSupport classesChartSupport) { this.classesChartSupport = classesChartSupport; if (classesChartSupport != null && source instanceof Snapshot) RequestProcessor.getDefault().post(new Runnable() { public void run() { File file = new File(source.getStorage().getDirectory(), CLASSES_CHART_STORAGE); if (file.isFile()) loadChartSupport(classesChartSupport, file); } }); } public void registerThreadsChartSupport(final SimpleXYChartSupport threadsChartSupport) { this.threadsChartSupport = threadsChartSupport; if (threadsChartSupport != null && source instanceof Snapshot) RequestProcessor.getDefault().post(new Runnable() { public void run() { File file = new File(source.getStorage().getDirectory(), THREADS_CHART_STORAGE); if (file.isFile()) loadChartSupport(threadsChartSupport, file); } }); } public synchronized void cleanup() { listeners.clear(); if (!initialized) return; if (jvm != null && monitoredDataListener != null) jvm.removeMonitoredDataListener(monitoredDataListener); } public void addChangeListener(ChangeListener listener) { if (live) listeners.add(listener); } public void removeChangeListener(ChangeListener listener) { if (live) listeners.remove(listener); } public void save(Snapshot snapshot) { initialize(); Storage storage = snapshot.getStorage(); setProperty(storage, SNAPSHOT_VERSION, CURRENT_SNAPSHOT_VERSION); setProperty(storage, PROP_CHART_CACHE, Integer.toString(chartCache)); setProperty(storage, PROP_UPTIME, Long.toString(uptime)); setProperty(storage, PROP_PREV_UPTIME, Long.toString(prevUpTime)); setProperty(storage, PROP_CPU_MONITORING_SUPPORTED, Boolean.toString(cpuMonitoringSupported)); setProperty(storage, PROP_GC_MONITORING_SUPPORTED, Boolean.toString(gcMonitoringSupported)); setProperty(storage, PROP_MEMORY_MONITORING_SUPPORTED, Boolean.toString(memoryMonitoringSupported)); setProperty(storage, PROP_CLASS_MONITORING_SUPPORTED, Boolean.toString(classMonitoringSupported)); setProperty(storage, PROP_THREADS_MONITORING_SUPPORTED, Boolean.toString(threadsMonitoringSupported)); setProperty(storage, PROP_NUMBER_OF_PROCESSORS, Integer.toString(processorsCount)); setProperty(storage, PROP_PROCESS_CPU_TIME, Long.toString(processCpuTime)); setProperty(storage, PROP_PROCESS_GC_TIME, Long.toString(processGcTime)); setProperty(storage, PROP_PREV_PROCESS_CPU_TIME, Long.toString(prevProcessCpuTime)); setProperty(storage, PROP_PREV_PROCESS_GC_TIME, Long.toString(prevProcessGcTime)); setProperty(storage, PROP_HEAP_NAME, heapName); setProperty(storage, PROP_HEAP_CAPACITY, Long.toString(heapCapacity)); setProperty(storage, PROP_HEAP_USED, Long.toString(heapUsed)); setProperty(storage, PROP_MAX_HEAP, Long.toString(maxHeap)); setProperty(storage, PROP_PERMGEN_NAME, permgenName); setProperty(storage, PROP_PERMGEN_CAPACITY, Long.toString(permgenCapacity)); setProperty(storage, PROP_PERMGEN_USED, Long.toString(permgenUsed)); setProperty(storage, PROP_PERMGEN_MAX, Long.toString(permgenMax)); setProperty(storage, PROP_SHARED_UNLOADED, Long.toString(sharedUnloaded)); setProperty(storage, PROP_TOTAL_UNLOADED, Long.toString(totalUnloaded)); setProperty(storage, PROP_SHARED_LOADED, Long.toString(sharedLoaded)); setProperty(storage, PROP_TOTAL_LOADED, Long.toString(totalLoaded)); setProperty(storage, PROP_TOTAL_THREADS, Long.toString(totalThreads)); setProperty(storage, PROP_DAEMON_THREADS, Long.toString(daemonThreads)); setProperty(storage, PROP_PEAK_THREADS, Long.toString(peakThreads)); setProperty(storage, PROP_STARTED_THREADS, Long.toString(startedThreads)); File dir = storage.getDirectory(); if (cpuMonitoringSupported || gcMonitoringSupported) saveChartSupport(cpuChartSupport, new File(dir, CPU_CHART_STORAGE)); if (memoryMonitoringSupported) saveChartSupport(heapChartSupport, new File(dir, HEAP_CHART_STORAGE)); if (memoryMonitoringSupported) saveChartSupport(permGenChartSupport, new File(dir, PERMGEN_CHART_STORAGE)); if (classMonitoringSupported) saveChartSupport(classesChartSupport, new File(dir, CLASSES_CHART_STORAGE)); if (threadsMonitoringSupported) saveChartSupport(threadsChartSupport, new File(dir, THREADS_CHART_STORAGE)); } private static void saveChartSupport(SimpleXYChartSupport chartSupport, File file) { if (chartSupport == null) return; OutputStream os = null; try { os = new FileOutputStream(file); chartSupport.saveValues(os); } catch (Exception e) { // TODO: log it } finally { try { if (os != null) os.close(); } catch (Exception e) { // TODO: log it } } } private static void loadChartSupport(SimpleXYChartSupport chartSupport, File file) { InputStream is = null; try { is = new FileInputStream(file); chartSupport.loadValues(is); } catch (Exception e) { // TODO: log it } finally { try { if (is != null) is.close(); } catch (Exception e) { // TODO: log it } } } private void initialize(Snapshot snapshot) { // TODO: if some property cannot be loaded for current snapshot version, FAIL initializing the snapshot! Storage storage = snapshot.getStorage(); String version = getProperty(storage, SNAPSHOT_VERSION); chartCache = Integer.parseInt(getProperty(storage, PROP_CHART_CACHE)); uptime = Long.parseLong(getProperty(storage, PROP_UPTIME)); prevUpTime = Long.parseLong(getProperty(storage, PROP_PREV_UPTIME)); takeHeapDumpSupported = false; cpuMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_CPU_MONITORING_SUPPORTED)); gcMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_GC_MONITORING_SUPPORTED)); memoryMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_MEMORY_MONITORING_SUPPORTED)); classMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_CLASS_MONITORING_SUPPORTED)); threadsMonitoringSupported = Boolean.parseBoolean(getProperty(storage, PROP_THREADS_MONITORING_SUPPORTED)); processorsCount = Integer.parseInt(getProperty(storage, PROP_NUMBER_OF_PROCESSORS)); processCpuTime = Long.parseLong(getProperty(storage, PROP_PROCESS_CPU_TIME)); processGcTime = Long.parseLong(getProperty(storage, PROP_PROCESS_GC_TIME)); prevProcessCpuTime = Long.parseLong(getProperty(storage, PROP_PREV_PROCESS_CPU_TIME)); prevProcessGcTime = Long.parseLong(getProperty(storage, PROP_PREV_PROCESS_GC_TIME)); heapCapacity = Long.parseLong(getProperty(storage, PROP_HEAP_CAPACITY)); heapUsed = Long.parseLong(getProperty(storage, PROP_HEAP_USED)); maxHeap = Long.parseLong(getProperty(storage, PROP_MAX_HEAP)); permgenCapacity = Long.parseLong(getProperty(storage, PROP_PERMGEN_CAPACITY)); permgenUsed = Long.parseLong(getProperty(storage, PROP_PERMGEN_USED)); permgenMax = Long.parseLong(getProperty(storage, PROP_PERMGEN_MAX)); sharedUnloaded = Long.parseLong(getProperty(storage, PROP_SHARED_UNLOADED)); totalUnloaded = Long.parseLong(getProperty(storage, PROP_TOTAL_UNLOADED)); sharedLoaded = Long.parseLong(getProperty(storage, PROP_SHARED_LOADED)); totalLoaded = Long.parseLong(getProperty(storage, PROP_TOTAL_LOADED)); totalThreads = Long.parseLong(getProperty(storage, PROP_TOTAL_THREADS)); daemonThreads = Long.parseLong(getProperty(storage, PROP_DAEMON_THREADS)); peakThreads = Long.parseLong(getProperty(storage, PROP_PEAK_THREADS)); startedThreads = Long.parseLong(getProperty(storage, PROP_STARTED_THREADS)); if (version.compareTo("1.1") >= 0) { // NOI18N heapName = getProperty(storage, PROP_HEAP_NAME); permgenName = getProperty(storage, PROP_PERMGEN_NAME); } else { heapName = NbBundle.getMessage(ApplicationMonitorModel.class, "LBL_Heap"); // NOI18N permgenName = NbBundle.getMessage(ApplicationMonitorModel.class, "LBL_PermGen"); // NOI18N } } private static void setProperty(Storage storage, String property, String value) { storage.setCustomProperty(property, value == null ? PROP_NOT_DEFINED : value); } private static String getProperty(Storage storage, String property) { String value = storage.getCustomProperty(property); return PROP_NOT_DEFINED.equals(value) ? null : value; } private void initialize(Application application) { GlobalPreferences preferences = GlobalPreferences.sharedInstance(); chartCache = preferences.getMonitoredDataCache() * 60 / preferences.getMonitoredDataPoll(); processorsCount = 1; jvm = JvmFactory.getJVMFor(application); if (jvm != null) { HeapDumpSupport hds = HeapDumpSupport.getInstance(); if (application.isLocalApplication()) { takeHeapDumpSupported = hds.supportsHeapDump(application); } else { takeHeapDumpSupported = hds.supportsRemoteHeapDump(application); } cpuMonitoringSupported = jvm.isCpuMonitoringSupported(); gcMonitoringSupported = jvm.isCollectionTimeSupported(); memoryMonitoringSupported = jvm.isMemoryMonitoringSupported(); classMonitoringSupported = jvm.isClassMonitoringSupported(); threadsMonitoringSupported = jvm.isThreadMonitoringSupported(); if (memoryMonitoringSupported) { String[] names = jvm.getGenName(); heapName = names[0]; permgenName = names[1]; } } memoryMXBean = null; JmxModel jmxModel = JmxModelFactory.getJmxModelFor(application); if (jmxModel != null && jmxModel.getConnectionState() == ConnectionState.CONNECTED) { JvmMXBeans mxbeans = JvmMXBeansFactory.getJvmMXBeans(jmxModel); if (mxbeans != null) { memoryMXBean = mxbeans.getMemoryMXBean(); OperatingSystemMXBean osbean = mxbeans.getOperatingSystemMXBean(); if (osbean != null) processorsCount = osbean.getAvailableProcessors(); } } if (jvm != null) { updateValues(System.currentTimeMillis(), jvm.getMonitoredData()); if (live) { monitoredDataListener = new MonitoredDataListener() { long lastTimestamp = -1; public void monitoredDataEvent(final MonitoredData data) { long timestamp = System.currentTimeMillis(); final long timestampF = lastTimestamp < timestamp ? lastTimestamp = timestamp : ++lastTimestamp; SwingUtilities.invokeLater(new Runnable() { public void run() { updateValues(timestampF, data); fireChange(); } }); } }; jvm.addMonitoredDataListener(monitoredDataListener); } } } private void updateValues(final long time, final MonitoredData data) { timestamp = time; if (data != null) { prevUpTime = uptime; uptime = data.getUpTime(); if (cpuMonitoringSupported) { prevProcessCpuTime = processCpuTime; processCpuTime = data.getProcessCpuTime(); } if (gcMonitoringSupported) { prevProcessGcTime = processGcTime; processGcTime = data.getCollectionTime(); } if (memoryMonitoringSupported) { heapCapacity = data.getGenCapacity()[0]; heapUsed = data.getGenUsed()[0]; maxHeap = data.getGenMaxCapacity()[0]; permgenCapacity = data.getGenCapacity()[1]; permgenUsed = data.getGenUsed()[1]; permgenMax = data.getGenMaxCapacity()[1]; } if (classMonitoringSupported) { sharedUnloaded = data.getSharedUnloadedClasses(); totalUnloaded = data.getUnloadedClasses(); sharedLoaded = data.getSharedLoadedClasses(); totalLoaded = data.getLoadedClasses(); } if (threadsMonitoringSupported) { totalThreads = data.getThreadsLive(); daemonThreads = data.getThreadsDaemon(); peakThreads = data.getThreadsLivePeak(); startedThreads = data.getThreadsStarted(); } } } private void fireChange() { final List<ChangeListener> list = new ArrayList(); synchronized (listeners) { list.addAll(listeners); } for (ChangeListener l : list) l.stateChanged(new ChangeEvent(this)); } private ApplicationMonitorModel(DataSource source, boolean live) { initialized = false; this.source = source; this.live = live; listeners = Collections.synchronizedList(new ArrayList()); } }