hadoop GcTimeMonitor 源码

  • 2022-10-20
  • 浏览 (215)

haddop GcTimeMonitor 代码

文件路径:/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/GcTimeMonitor.java

/**
 * 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.hadoop.util;

import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * This class monitors the percentage of time the JVM is paused in GC within
 * the specified observation window, say 1 minute. The user can provide a
 * hook which will be called whenever this percentage exceeds the specified
 * threshold.
 */
public class GcTimeMonitor extends Thread {

  private final long maxGcTimePercentage;
  private final long observationWindowMs, sleepIntervalMs;
  private final GcTimeAlertHandler alertHandler;

  private final List<GarbageCollectorMXBean> gcBeans =
      ManagementFactory.getGarbageCollectorMXBeans();
  // Ring buffers containing GC timings and timestamps when timings were taken
  private final TsAndData[] gcDataBuf;
  private int bufSize, startIdx, endIdx;

  private long startTime;
  private final GcData curData = new GcData();
  private volatile boolean shouldRun = true;

  public static class Builder {

    private long observationWindowMs = TimeUnit.MINUTES.toMillis(1);
    private long sleepIntervalMs = TimeUnit.SECONDS.toMillis(5);
    private int maxGcTimePercentage = 100;
    private GcTimeAlertHandler handler = null;

    /**
     * Set observation window size in milliseconds.
     * @param value value.
     * @return window size in milliseconds.
     */
    public Builder observationWindowMs(long value) {
      this.observationWindowMs = value;
      return this;
    }

    /**
     * Set sleep interval in milliseconds.
     * @param value value.
     * @return IntervalMs.
     */
    public Builder sleepIntervalMs(long value) {
      this.sleepIntervalMs = value;
      return this;
    }

    /**
     * Set the max GC time percentage that triggers the alert handler.
     * @param value value.
     * @return max GC time percentage.
     */
    public Builder maxGcTimePercentage(int value) {
      this.maxGcTimePercentage = value;
      return this;
    }

    /**
     * Set the GC alert handler.
     * @param value value.
     * @return GC alert handler.
     */
    public Builder gcTimeAlertHandler(GcTimeAlertHandler value) {
      this.handler = value;
      return this;
    }

    public GcTimeMonitor build() {
      return new GcTimeMonitor(observationWindowMs, sleepIntervalMs,
          maxGcTimePercentage, handler);
    }
  }


  /**
   * Create an instance of GCTimeMonitor. Once it's started, it will stay alive
   * and monitor GC time percentage until shutdown() is called. If you don't
   * put a limit on the number of GCTimeMonitor instances that you create, and
   * alertHandler != null, you should necessarily call shutdown() once the given
   * instance is not needed. Otherwise, you may create a memory leak, because
   * each running GCTimeMonitor will keep its alertHandler object in memory,
   * which in turn may reference and keep in memory many more other objects.
   *
   * @param observationWindowMs the interval over which the percentage
   *   of GC time should be calculated. A practical value would be somewhere
   *   between 30 sec and several minutes.
   * @param sleepIntervalMs how frequently this thread should wake up to check
   *   GC timings. This is also a frequency with which alertHandler will be
   *   invoked if GC time percentage exceeds the specified limit. A practical
   *   value would likely be 500..1000 ms.
   * @param maxGcTimePercentage A GC time percentage limit (0..100) within
   *   observationWindowMs. Once this is exceeded, alertHandler will be
   *   invoked every sleepIntervalMs milliseconds until GC time percentage
   *   falls below this limit.
   * @param alertHandler a single method in this interface is invoked when GC
   *   time percentage exceeds the specified limit.
   */
  public GcTimeMonitor(long observationWindowMs, long sleepIntervalMs,
      int maxGcTimePercentage, GcTimeAlertHandler alertHandler) {
    Preconditions.checkArgument(observationWindowMs > 0);
    Preconditions.checkArgument(
        sleepIntervalMs > 0 && sleepIntervalMs < observationWindowMs);
    Preconditions.checkArgument(
        maxGcTimePercentage >= 0 && maxGcTimePercentage <= 100);

    this.observationWindowMs = observationWindowMs;
    this.sleepIntervalMs = sleepIntervalMs;
    this.maxGcTimePercentage = maxGcTimePercentage;
    this.alertHandler = alertHandler;

    bufSize = (int) (observationWindowMs / sleepIntervalMs + 2);
    // Prevent the user from accidentally creating an abnormally big buffer,
    // which will result in slow calculations and likely inaccuracy.
    Preconditions.checkArgument(bufSize <= 128 * 1024);
    gcDataBuf = new TsAndData[bufSize];
    for (int i = 0; i < bufSize; i++) {
      gcDataBuf[i] = new TsAndData();
    }

    this.setDaemon(true);
    this.setName("GcTimeMonitor obsWindow = " + observationWindowMs +
        ", sleepInterval = " + sleepIntervalMs +
        ", maxGcTimePerc = " + maxGcTimePercentage);
  }

  @Override
  public void run() {
    startTime = System.currentTimeMillis();
    curData.timestamp = startTime;
    gcDataBuf[startIdx].setValues(startTime, 0);

    while (shouldRun) {
      try {
        Thread.sleep(sleepIntervalMs);
      } catch (InterruptedException ie) {
        return;
      }

      calculateGCTimePercentageWithinObservedInterval();
      if (alertHandler != null &&
          curData.gcTimePercentage > maxGcTimePercentage) {
        alertHandler.alert(curData.clone());
      }
    }
  }

  public void shutdown() {
    shouldRun = false;
  }

  /**
   * Returns a copy of the most recent data measured by this monitor.
   * @return a copy of the most recent data measured by this monitor
   */
  public GcData getLatestGcData() {
    return curData.clone();
  }

  private void calculateGCTimePercentageWithinObservedInterval() {
    long prevTotalGcTime = curData.totalGcTime;
    long totalGcTime = 0;
    long totalGcCount = 0;
    for (GarbageCollectorMXBean gcBean : gcBeans) {
      totalGcTime += gcBean.getCollectionTime();
      totalGcCount += gcBean.getCollectionCount();
    }
    long gcTimeWithinSleepInterval = totalGcTime - prevTotalGcTime;

    long ts = System.currentTimeMillis();
    long gcMonitorRunTime = ts - startTime;

    endIdx = (endIdx + 1) % bufSize;
    gcDataBuf[endIdx].setValues(ts, gcTimeWithinSleepInterval);

    // Move startIdx forward until we reach the first buffer entry with
    // timestamp within the observation window.
    long startObsWindowTs = ts - observationWindowMs;
    while (gcDataBuf[startIdx].ts < startObsWindowTs && startIdx != endIdx) {
      startIdx = (startIdx + 1) % bufSize;
    }

    // Calculate total GC time within observationWindowMs.
    // We should be careful about GC time that passed before the first timestamp
    // in our observation window.
    long gcTimeWithinObservationWindow = Math.min(
        gcDataBuf[startIdx].gcPause, gcDataBuf[startIdx].ts - startObsWindowTs);
    if (startIdx != endIdx) {
      for (int i = (startIdx + 1) % bufSize; i != endIdx;
           i = (i + 1) % bufSize) {
        gcTimeWithinObservationWindow += gcDataBuf[i].gcPause;
      }
    }

    curData.update(ts, gcMonitorRunTime, totalGcTime, totalGcCount,
        (int) (gcTimeWithinObservationWindow * 100 /
          Math.min(observationWindowMs, gcMonitorRunTime)));
  }

  /**
   * The user can provide an instance of a class implementing this interface
   * when initializing a GcTimeMonitor to receive alerts when GC time
   * percentage exceeds the specified threshold.
   */
  public interface GcTimeAlertHandler {
    void alert(GcData gcData);
  }

  /** Encapsulates data about GC pauses measured at the specific timestamp. */
  public static class GcData implements Cloneable {
    private long timestamp;
    private long gcMonitorRunTime, totalGcTime, totalGcCount;
    private int gcTimePercentage;

    /**
     * Returns the absolute timestamp when this measurement was taken.
     * @return timestamp.
     */
    public long getTimestamp() {
      return timestamp;
    }

    /**
     * Returns the time since the start of the associated GcTimeMonitor.
     * @return GcMonitorRunTime.
     */
    public long getGcMonitorRunTime() {
      return gcMonitorRunTime;
    }

    /**
     * Returns accumulated GC time since this JVM started.
     * @return AccumulatedGcTime.
     */
    public long getAccumulatedGcTime() {
      return totalGcTime;
    }

    /**
     * Returns the accumulated number of GC pauses since this JVM started.
     * @return AccumulatedGcCount.
     */
    public long getAccumulatedGcCount() {
      return totalGcCount;
    }

    /**
     * Returns the percentage (0..100) of time that the JVM spent in GC pauses
     * within the observation window of the associated GcTimeMonitor.
     *
     * @return GcTimePercentage.
     */
    public int getGcTimePercentage() {
      return gcTimePercentage;
    }

    private synchronized void update(long inTimestamp, long inGcMonitorRunTime,
        long inTotalGcTime, long inTotalGcCount, int inGcTimePercentage) {
      this.timestamp = inTimestamp;
      this.gcMonitorRunTime = inGcMonitorRunTime;
      this.totalGcTime = inTotalGcTime;
      this.totalGcCount = inTotalGcCount;
      this.gcTimePercentage = inGcTimePercentage;
    }

    @Override
    public synchronized GcData clone() {
      try {
        return (GcData) super.clone();
      } catch (CloneNotSupportedException e) {
        throw new RuntimeException(e);
      }
    }
  }

  private static class TsAndData {
    private long ts;      // Timestamp when this measurement was taken
    private long gcPause; // Total GC pause time within the interval between ts
                          // and the timestamp of the previous measurement.

    void setValues(long inTs, long inGcPause) {
      this.ts = inTs;
      this.gcPause = inGcPause;
    }
  }
}

相关信息

hadoop 源码目录

相关文章

hadoop ApplicationClassLoader 源码

hadoop AsyncDiskService 源码

hadoop AutoCloseableLock 源码

hadoop BasicDiskValidator 源码

hadoop BlockingThreadPoolExecutorService 源码

hadoop CacheableIPList 源码

hadoop ChunkedArrayList 源码

hadoop ClassUtil 源码

hadoop Classpath 源码

hadoop CleanerUtil 源码

0  赞