ResourceUtilizationUtils.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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.fs.azurebfs.utils;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

import com.sun.management.OperatingSystemMXBean;

import org.apache.hadoop.classification.VisibleForTesting;

import static org.apache.hadoop.fs.azurebfs.constants.FileSystemConfigurations.BYTES_PER_GIGABYTE;

/**
 * Utility class for retrieving JVM- and system-level resource utilization
 * metrics such as CPU load, memory usage, and available heap memory.
 * All metrics are returned as long values with 2-decimal precision stored as integer (scaled by 100).
 */
public final class ResourceUtilizationUtils {

  private static final long SCALE_FACTOR = 100L; // 2 decimal places

  private ResourceUtilizationUtils() {
    // Prevent instantiation
  }

  /**
   * Scales a double value by {@link #SCALE_FACTOR} to store 2-decimal precision as long.
   *
   * @param value value to scale
   * @return scaled long value
   */
  private static long scale(double value) {
    return Math.round(value * SCALE_FACTOR);
  }

  /**
   * Returns the available heap memory in gigabytes, calculated as the difference between
   * the committed heap and used heap memory.
   * <p>
   * The result is scaled by 100 for 2-decimal precision.
   * </p>
   *
   * @return available heap memory in GB (scaled by 100)
   */
  public static long getAvailableHeapMemory() {
    MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
    double gb = (mu.getCommitted() - mu.getUsed()) / (double) BYTES_PER_GIGABYTE;
    return scale(gb);
  }

  /**
   * Returns the JVM heap memory currently committed.
   * <p>
   * Committed memory is the amount of memory guaranteed to be available for the JVM.
   * </p>
   *
   * @return committed heap memory in GB (scaled by 100)
   */
  @VisibleForTesting
  public static long getCommittedHeapMemory() {
    MemoryMXBean osBean = ManagementFactory.getMemoryMXBean();
    MemoryUsage memoryUsage = osBean.getHeapMemoryUsage();
    double gb = memoryUsage.getCommitted() / (double) BYTES_PER_GIGABYTE;
    return scale(gb);
  }

  /**
   * Returns the system-wide CPU load as a fraction (scaled by 100 for 2-decimal precision).
   * <p>
   * The value ranges between 0 (no load) and 100 (full load). Returns 0 if CPU load cannot be obtained.
   * </p>
   *
   * @return system CPU load (scaled by 100)
   */
  @VisibleForTesting
  public static long getSystemCpuLoad() {
    OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
    double cpuLoad = osBean.getSystemCpuLoad();
    if (cpuLoad < 0) {
      return 0L;
    }
    return scale(cpuLoad);
  }

  /**
   * Returns the JVM process CPU load as a fraction (scaled by 100 for 2-decimal precision).
   * <p>
   * The value ranges between 0 (no load) and 100 (full CPU used by this process). Returns 0 if CPU load cannot be obtained.
   * </p>
   *
   * @return JVM process CPU load (scaled by 100)
   */
  @VisibleForTesting
  public static long getJvmCpuLoad() {
    OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
    double cpuLoad = osBean.getProcessCpuLoad();
    if (cpuLoad < 0) {
      return 0L;
    }
    return scale(cpuLoad);
  }

  /**
   * Returns the heap memory usage as a fraction of max heap (scaled by 100).
   *
   * @return memory load (used/max heap) scaled by 100
   */
  @VisibleForTesting
  public static long getMemoryLoad() {
    MemoryMXBean osBean = ManagementFactory.getMemoryMXBean();
    MemoryUsage memoryUsage = osBean.getHeapMemoryUsage();
    double memLoad = (double) memoryUsage.getUsed() / memoryUsage.getMax();
    return scale(memLoad);
  }

  /**
   * Returns the currently used heap memory in gigabytes.
   *
   * @return used heap memory in GB (scaled by 100)
   */
  public static long getUsedHeapMemory() {
    MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
    double gb = mu.getUsed() / (double) BYTES_PER_GIGABYTE;
    return scale(gb);
  }

  /**
   * Returns the maximum heap memory that the JVM can use.
   *
   * @return max heap memory in GB (scaled by 100)
   */
  public static long getMaxHeapMemory() {
    MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
    double gb = mu.getMax() / (double) BYTES_PER_GIGABYTE;
    return scale(gb);
  }

  /**
   * Returns the available heap memory relative to the max heap.
   * <p>
   * This method calculates the difference between max heap and currently used heap,
   * then converts it to gigabytes rounded up.
   * </p>
   *
   * @return available heap memory in GB (rounded up)
   */
  public static long getAvailableMaxHeapMemory() {
    MemoryMXBean osBean = ManagementFactory.getMemoryMXBean();
    MemoryUsage memoryUsage = osBean.getHeapMemoryUsage();
    long availableHeapBytes = memoryUsage.getMax() - memoryUsage.getUsed();
    return (availableHeapBytes + BYTES_PER_GIGABYTE - 1) / BYTES_PER_GIGABYTE;
  }
}