Import the GCInspectorMXBean from origin
Although there is little relevant information in the GC inspector, some application like cassandra-stress looks for it and fails if it cannot be found. This patch import the GCInspectorMBean and its implementation. Signed-off-by: Amnon Heiman <amnon@scylladb.com>
This commit is contained in:
parent
ba0ed7cbc7
commit
7b9ea44354
280
src/main/java/org/apache/cassandra/service/GCInspector.java
Normal file
280
src/main/java/org/apache/cassandra/service/GCInspector.java
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 ScyllaDB
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Moddified by ScyllaDB
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cassandra.service;
|
||||||
|
|
||||||
|
import java.lang.management.GarbageCollectorMXBean;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.lang.management.MemoryUsage;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javax.management.MBeanServer;
|
||||||
|
import javax.management.Notification;
|
||||||
|
import javax.management.NotificationListener;
|
||||||
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.openmbean.CompositeData;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.sun.management.GarbageCollectionNotificationInfo;
|
||||||
|
import com.sun.management.GcInfo;
|
||||||
|
|
||||||
|
public class GCInspector implements NotificationListener, GCInspectorMXBean
|
||||||
|
{
|
||||||
|
public static final String MBEAN_NAME = "org.apache.cassandra.service:type=GCInspector";
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(GCInspector.class);
|
||||||
|
final static long MIN_LOG_DURATION = 200;
|
||||||
|
final static long MIN_LOG_DURATION_TPSTATS = 1000;
|
||||||
|
|
||||||
|
static final class State
|
||||||
|
{
|
||||||
|
final double maxRealTimeElapsed;
|
||||||
|
final double totalRealTimeElapsed;
|
||||||
|
final double sumSquaresRealTimeElapsed;
|
||||||
|
final double totalBytesReclaimed;
|
||||||
|
final double count;
|
||||||
|
final long startNanos;
|
||||||
|
|
||||||
|
State(double extraElapsed, double extraBytes, State prev)
|
||||||
|
{
|
||||||
|
this.totalRealTimeElapsed = prev.totalRealTimeElapsed + extraElapsed;
|
||||||
|
this.totalBytesReclaimed = prev.totalBytesReclaimed + extraBytes;
|
||||||
|
this.sumSquaresRealTimeElapsed = prev.sumSquaresRealTimeElapsed + (extraElapsed * extraElapsed);
|
||||||
|
this.startNanos = prev.startNanos;
|
||||||
|
this.count = prev.count + 1;
|
||||||
|
this.maxRealTimeElapsed = Math.max(prev.maxRealTimeElapsed, extraElapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
State()
|
||||||
|
{
|
||||||
|
count = maxRealTimeElapsed = sumSquaresRealTimeElapsed = totalRealTimeElapsed = totalBytesReclaimed = 0;
|
||||||
|
startNanos = System.nanoTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class GCState
|
||||||
|
{
|
||||||
|
final GarbageCollectorMXBean gcBean;
|
||||||
|
final boolean assumeGCIsPartiallyConcurrent;
|
||||||
|
final boolean assumeGCIsOldGen;
|
||||||
|
private String[] keys;
|
||||||
|
long lastGcTotalDuration = 0;
|
||||||
|
|
||||||
|
|
||||||
|
GCState(GarbageCollectorMXBean gcBean, boolean assumeGCIsPartiallyConcurrent, boolean assumeGCIsOldGen)
|
||||||
|
{
|
||||||
|
this.gcBean = gcBean;
|
||||||
|
this.assumeGCIsPartiallyConcurrent = assumeGCIsPartiallyConcurrent;
|
||||||
|
this.assumeGCIsOldGen = assumeGCIsOldGen;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] keys(GarbageCollectionNotificationInfo info)
|
||||||
|
{
|
||||||
|
if (keys != null)
|
||||||
|
return keys;
|
||||||
|
|
||||||
|
keys = info.getGcInfo().getMemoryUsageBeforeGc().keySet().toArray(new String[0]);
|
||||||
|
Arrays.sort(keys);
|
||||||
|
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final AtomicReference<State> state = new AtomicReference<>(new State());
|
||||||
|
|
||||||
|
final Map<String, GCState> gcStates = new HashMap<>();
|
||||||
|
|
||||||
|
public GCInspector()
|
||||||
|
{
|
||||||
|
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ObjectName gcName = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
|
||||||
|
for (ObjectName name : mbs.queryNames(gcName, null))
|
||||||
|
{
|
||||||
|
GarbageCollectorMXBean gc = ManagementFactory.newPlatformMXBeanProxy(mbs, name.getCanonicalName(), GarbageCollectorMXBean.class);
|
||||||
|
gcStates.put(gc.getName(), new GCState(gc, assumeGCIsPartiallyConcurrent(gc), assumeGCIsOldGen(gc)));
|
||||||
|
}
|
||||||
|
|
||||||
|
mbs.registerMBean(this, new ObjectName(MBEAN_NAME));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void register() throws Exception
|
||||||
|
{
|
||||||
|
GCInspector inspector = new GCInspector();
|
||||||
|
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
|
||||||
|
ObjectName gcName = new ObjectName(ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE + ",*");
|
||||||
|
for (ObjectName name : server.queryNames(gcName, null))
|
||||||
|
{
|
||||||
|
server.addNotificationListener(name, inspector, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assume that a GC type is at least partially concurrent and so a side channel method
|
||||||
|
* should be used to calculate application stopped time due to the GC.
|
||||||
|
*
|
||||||
|
* If the GC isn't recognized then assume that is concurrent and we need to do our own calculation
|
||||||
|
* via the the side channel.
|
||||||
|
*/
|
||||||
|
private static boolean assumeGCIsPartiallyConcurrent(GarbageCollectorMXBean gc)
|
||||||
|
{
|
||||||
|
switch (gc.getName())
|
||||||
|
{
|
||||||
|
//First two are from the serial collector
|
||||||
|
case "Copy":
|
||||||
|
case "MarkSweepCompact":
|
||||||
|
//Parallel collector
|
||||||
|
case "PS MarkSweep":
|
||||||
|
case "PS Scavenge":
|
||||||
|
case "G1 Young Generation":
|
||||||
|
//CMS young generation collector
|
||||||
|
case "ParNew":
|
||||||
|
return false;
|
||||||
|
case "ConcurrentMarkSweep":
|
||||||
|
case "G1 Old Generation":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
//Assume possibly concurrent if unsure
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assume that a GC type is an old generation collection so SSTableDeletingTask.rescheduleFailedTasks()
|
||||||
|
* should be invoked.
|
||||||
|
*
|
||||||
|
* Defaults to not invoking SSTableDeletingTask.rescheduleFailedTasks() on unrecognized GC names
|
||||||
|
*/
|
||||||
|
private static boolean assumeGCIsOldGen(GarbageCollectorMXBean gc)
|
||||||
|
{
|
||||||
|
switch (gc.getName())
|
||||||
|
{
|
||||||
|
case "Copy":
|
||||||
|
case "PS Scavenge":
|
||||||
|
case "G1 Young Generation":
|
||||||
|
case "ParNew":
|
||||||
|
return false;
|
||||||
|
case "MarkSweepCompact":
|
||||||
|
case "PS MarkSweep":
|
||||||
|
case "ConcurrentMarkSweep":
|
||||||
|
case "G1 Old Generation":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
//Assume not old gen otherwise, don't call
|
||||||
|
//SSTableDeletingTask.rescheduleFailedTasks()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleNotification(final Notification notification, final Object handback)
|
||||||
|
{
|
||||||
|
String type = notification.getType();
|
||||||
|
if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION))
|
||||||
|
{
|
||||||
|
// retrieve the garbage collection notification information
|
||||||
|
CompositeData cd = (CompositeData) notification.getUserData();
|
||||||
|
GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from(cd);
|
||||||
|
String gcName = info.getGcName();
|
||||||
|
GcInfo gcInfo = info.getGcInfo();
|
||||||
|
|
||||||
|
long duration = gcInfo.getDuration();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The duration supplied in the notification info includes more than just
|
||||||
|
* application stopped time for concurrent GCs. Try and do a better job coming up with a good stopped time
|
||||||
|
* value by asking for and tracking cumulative time spent blocked in GC.
|
||||||
|
*/
|
||||||
|
GCState gcState = gcStates.get(gcName);
|
||||||
|
if (gcState.assumeGCIsPartiallyConcurrent)
|
||||||
|
{
|
||||||
|
long previousTotal = gcState.lastGcTotalDuration;
|
||||||
|
long total = gcState.gcBean.getCollectionTime();
|
||||||
|
gcState.lastGcTotalDuration = total;
|
||||||
|
duration = total - previousTotal; // may be zero for a really fast collection
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(info.getGcName()).append(" GC in ").append(duration).append("ms. ");
|
||||||
|
long bytes = 0;
|
||||||
|
Map<String, MemoryUsage> beforeMemoryUsage = gcInfo.getMemoryUsageBeforeGc();
|
||||||
|
Map<String, MemoryUsage> afterMemoryUsage = gcInfo.getMemoryUsageAfterGc();
|
||||||
|
for (String key : gcState.keys(info))
|
||||||
|
{
|
||||||
|
MemoryUsage before = beforeMemoryUsage.get(key);
|
||||||
|
MemoryUsage after = afterMemoryUsage.get(key);
|
||||||
|
if (after != null && after.getUsed() != before.getUsed())
|
||||||
|
{
|
||||||
|
sb.append(key).append(": ").append(before.getUsed());
|
||||||
|
sb.append(" -> ");
|
||||||
|
sb.append(after.getUsed());
|
||||||
|
if (!key.equals(gcState.keys[gcState.keys.length - 1]))
|
||||||
|
sb.append("; ");
|
||||||
|
bytes += before.getUsed() - after.getUsed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
State prev = state.get();
|
||||||
|
if (state.compareAndSet(prev, new State(duration, bytes, prev)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String st = sb.toString();
|
||||||
|
if (duration > MIN_LOG_DURATION)
|
||||||
|
logger.info(st);
|
||||||
|
else if (logger.isDebugEnabled())
|
||||||
|
logger.debug(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getTotalSinceLastCheck()
|
||||||
|
{
|
||||||
|
return state.getAndSet(new State());
|
||||||
|
}
|
||||||
|
|
||||||
|
public double[] getAndResetStats()
|
||||||
|
{
|
||||||
|
State state = getTotalSinceLastCheck();
|
||||||
|
double[] r = new double[6];
|
||||||
|
r[0] = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - state.startNanos);
|
||||||
|
r[1] = state.maxRealTimeElapsed;
|
||||||
|
r[2] = state.totalRealTimeElapsed;
|
||||||
|
r[3] = state.sumSquaresRealTimeElapsed;
|
||||||
|
r[4] = state.totalBytesReclaimed;
|
||||||
|
r[5] = state.count;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.apache.cassandra.service;
|
||||||
|
|
||||||
|
public interface GCInspectorMXBean
|
||||||
|
{
|
||||||
|
// returns { interval (ms), max(gc real time (ms)), sum(gc real time (ms)), sum((gc real time (ms))^2), sum(gc bytes), count(gc) }
|
||||||
|
public double[] getAndResetStats();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user