From c00ff38a3357ec52ee6df118dbae66aeba851050 Mon Sep 17 00:00:00 2001 From: Stefan Miklosovic Date: Thu, 27 Nov 2025 16:57:29 +0100 Subject: [PATCH 1/2] patch --- .../apache/cassandra/service/GCInspector.java | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/java/org/apache/cassandra/service/GCInspector.java b/src/java/org/apache/cassandra/service/GCInspector.java index 8f922156ca42..bc899428ea1d 100644 --- a/src/java/org/apache/cassandra/service/GCInspector.java +++ b/src/java/org/apache/cassandra/service/GCInspector.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.management.MBeanServer; @@ -58,34 +59,26 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean * The field from java.nio.Bits that tracks the total number of allocated * bytes of direct memory requires via ByteBuffer.allocateDirect that have not been GCed. */ - final static Field BITS_TOTAL_CAPACITY; + final static Field BITS_TOTAL_CAPACITY_JAVA_8; + final static Field BITS_TOTAL_CAPACITY_JAVA_11; - static { - Field temp = null; + Field totalTempFieldJava8 = null; + Field totalTempFieldJava11 = null; try { Class bitsClass = Class.forName("java.nio.Bits"); - Field f; - try - { - f = bitsClass.getDeclaredField("totalCapacity"); - } - catch (NoSuchFieldException ex) - { - // in Java11 it changed name to "TOTAL_CAPACITY" - f = bitsClass.getDeclaredField("TOTAL_CAPACITY"); - } - f.setAccessible(true); - temp = f; + totalTempFieldJava8 = getField(bitsClass, "totalCapacity"); + totalTempFieldJava11 = getField(bitsClass, "TOTAL_CAPACITY"); } catch (Throwable t) { logger.debug("Error accessing field of java.nio.Bits", t); //Don't care, will just return the dummy value -1 if we can't get at the field in this JVM } - BITS_TOTAL_CAPACITY = temp; + BITS_TOTAL_CAPACITY_JAVA_8 = totalTempFieldJava8; + BITS_TOTAL_CAPACITY_JAVA_11 = totalTempFieldJava11; } static final class State @@ -326,14 +319,43 @@ public double[] getAndResetStats() private static long getAllocatedDirectMemory() { - if (BITS_TOTAL_CAPACITY == null) return -1; + long fieldValue = getFieldValue(BITS_TOTAL_CAPACITY_JAVA_8, true); + + if (fieldValue == -1) + fieldValue = getFieldValue(BITS_TOTAL_CAPACITY_JAVA_11, true); + + return fieldValue; + } + + private static Field getField(Class clazz, String fieldName) + { + try + { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return field; + } + catch (Throwable t) + { + logger.trace("Error accessing field {} of {}", fieldName, clazz.getName(), t); + // Return null to indicate failure + return null; + } + } + + /** + * This method works well with JDK 8/11 + */ + private static long getFieldValue(Field field, boolean isAtomicLong) + { + if (field == null) return -1; try { - return BITS_TOTAL_CAPACITY.getLong(null); + return isAtomicLong ? ((AtomicLong) field.get(null)).get() : field.getLong(null); } catch (Throwable t) { - logger.trace("Error accessing field of java.nio.Bits", t); + logger.trace("Error accessing field value of {}", field.getName(), t); //Don't care how or why we failed to get the value in this JVM. Return -1 to indicate failure return -1; } From 6f9054a5ca05fc6e359f8bfd4c76bcc6cb01762e Mon Sep 17 00:00:00 2001 From: Stefan Miklosovic Date: Mon, 1 Dec 2025 12:26:04 +0100 Subject: [PATCH 2/2] fixes --- .../apache/cassandra/service/GCInspector.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/java/org/apache/cassandra/service/GCInspector.java b/src/java/org/apache/cassandra/service/GCInspector.java index bc899428ea1d..214a8a172f3c 100644 --- a/src/java/org/apache/cassandra/service/GCInspector.java +++ b/src/java/org/apache/cassandra/service/GCInspector.java @@ -57,28 +57,34 @@ public class GCInspector implements NotificationListener, GCInspectorMXBean /* * The field from java.nio.Bits that tracks the total number of allocated - * bytes of direct memory requires via ByteBuffer.allocateDirect that have not been GCed. + * bytes of direct memory requested via ByteBuffer.allocateDirect that have not been GCed. */ final static Field BITS_TOTAL_CAPACITY_JAVA_8; final static Field BITS_TOTAL_CAPACITY_JAVA_11; static { - Field totalTempFieldJava8 = null; - Field totalTempFieldJava11 = null; + Class bitsClass = null; + try { - Class bitsClass = Class.forName("java.nio.Bits"); - totalTempFieldJava8 = getField(bitsClass, "totalCapacity"); - totalTempFieldJava11 = getField(bitsClass, "TOTAL_CAPACITY"); + bitsClass = Class.forName("java.nio.Bits"); } catch (Throwable t) { - logger.debug("Error accessing field of java.nio.Bits", t); - //Don't care, will just return the dummy value -1 if we can't get at the field in this JVM + logger.debug("Error returning class of java.nio.Bits", t); + } + + if (bitsClass != null) + { + BITS_TOTAL_CAPACITY_JAVA_8 = getField(bitsClass, "totalCapacity"); + BITS_TOTAL_CAPACITY_JAVA_11 = getField(bitsClass, "TOTAL_CAPACITY"); + } + else + { + BITS_TOTAL_CAPACITY_JAVA_8 = null; + BITS_TOTAL_CAPACITY_JAVA_11 = null; } - BITS_TOTAL_CAPACITY_JAVA_8 = totalTempFieldJava8; - BITS_TOTAL_CAPACITY_JAVA_11 = totalTempFieldJava11; } static final class State @@ -344,7 +350,11 @@ private static Field getField(Class clazz, String fieldName) } /** - * This method works well with JDK 8/11 + * Retrieves the value of a Field, handling both regular long fields and AtomicLong fields. + * + * @param field the Field to retrieve the value from + * @param isAtomicLong true if the field is an AtomicLong, false if it's a regular long + * @return the field value, or -1 if retrieval fails or field is null. */ private static long getFieldValue(Field field, boolean isAtomicLong) {