Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public final class ClassFile {
private static final byte[] RUNTIME_ANNOTATIONS = "RuntimeVisibleAnnotations".getBytes(US_ASCII);

// reduce size of outlines by only extracting interesting annotations
private static final Object annotationsLock = new Object();
private static final Map<String, UtfKey> annotationKeys = new HashMap<>();
private static volatile Map<UtfKey, String> annotationsOfInterest;

Expand Down Expand Up @@ -95,17 +96,19 @@ public static ClassOutline outline(byte[] bytecode, int offset) {
*
* @param internalName the annotation type in internal form
*/
public static synchronized void annotationOfInterest(String internalName) {
if (annotationKeys.containsKey(internalName)) {
return; // already flagged as interesting
}
Map<UtfKey, String> ofInterest = new HashMap<>();
if (annotationsOfInterest != null) {
ofInterest.putAll(annotationsOfInterest); // copy on write
public static void annotationOfInterest(String internalName) {
synchronized (annotationsLock) {
if (annotationKeys.containsKey(internalName)) {
return; // already flagged as interesting
}
Map<UtfKey, String> ofInterest = new HashMap<>();
if (annotationsOfInterest != null) {
ofInterest.putAll(annotationsOfInterest); // copy on write
}
ofInterest.put(
annotationKeys.computeIfAbsent(internalName, ClassFile::annotationKey), internalName);
annotationsOfInterest = ofInterest;
}
ofInterest.put(
annotationKeys.computeIfAbsent(internalName, ClassFile::annotationKey), internalName);
annotationsOfInterest = ofInterest;
}

/**
Expand All @@ -115,19 +118,21 @@ public static synchronized void annotationOfInterest(String internalName) {
*
* @param internalNames the annotation types in internal form
*/
public static synchronized void annotationsOfInterest(Collection<String> internalNames) {
if (annotationKeys.keySet().containsAll(internalNames)) {
return; // already flagged as interesting
}
Map<UtfKey, String> ofInterest = new HashMap<>();
if (annotationsOfInterest != null) {
ofInterest.putAll(annotationsOfInterest); // copy on write
}
for (String internalName : internalNames) {
ofInterest.put(
annotationKeys.computeIfAbsent(internalName, ClassFile::annotationKey), internalName);
public static void annotationsOfInterest(Collection<String> internalNames) {
synchronized (annotationsLock) {
if (annotationKeys.keySet().containsAll(internalNames)) {
return; // already flagged as interesting
}
Map<UtfKey, String> ofInterest = new HashMap<>();
if (annotationsOfInterest != null) {
ofInterest.putAll(annotationsOfInterest); // copy on write
}
for (String internalName : internalNames) {
ofInterest.put(
annotationKeys.computeIfAbsent(internalName, ClassFile::annotationKey), internalName);
}
annotationsOfInterest = ofInterest;
}
annotationsOfInterest = ofInterest;
}

/** Create "modified-UTF8" key to make it easier to match annotations. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public final class GlobalObjectStore {
/** Threshold at which we start sampling keys to track old content. */
private static final int OLD_KEYS_THRESHOLD = 512;

private static final Object staleEntriesLock = new Object();

private static final Map<StoreKey, Object> weakMap = new ConcurrentHashMap<>();

private static final Set<StoreKey> oldKeys = new HashSet<>();
Expand All @@ -51,55 +53,56 @@ private GlobalObjectStore() {}
*
* @return the estimated remaining size of the global object-store
*/
public static synchronized int removeStaleEntries() {

Map<StoreKey, Object> weakMap = GlobalObjectStore.weakMap;
int estimatedSize = weakMap.size(); // capture size before any cleanup
StoreKey key;
while ((key = StoreKey.pollStaleKeys()) != null) {
if (weakMap.remove(key) != null) {
estimatedSize--;
public static int removeStaleEntries() {
synchronized (staleEntriesLock) {
Map<StoreKey, Object> weakMap = GlobalObjectStore.weakMap;
int estimatedSize = weakMap.size(); // capture size before any cleanup
StoreKey key;
while ((key = StoreKey.pollStaleKeys()) != null) {
if (weakMap.remove(key) != null) {
estimatedSize--;
}
}
}

// The following code handles proactively removing old content in an attempt to guide the
// store below its soft limit. We remove older objects before recent additions, assuming
// that older objects are less likely to be used. For performance reasons this only runs
// after observed periods of growth or reduction, or if the store is near its hard limit.
// The following code handles proactively removing old content in an attempt to guide the
// store below its soft limit. We remove older objects before recent additions, assuming
// that older objects are less likely to be used. For performance reasons this only runs
// after observed periods of growth or reduction, or if the store is near its hard limit.

// We deliberately avoid tracking exact age, and instead regularly sample keys to maintain
// a small set that we know are still alive after a couple of calls to removeStaleEntries.
// We deliberately avoid tracking exact age, and instead regularly sample keys to maintain
// a small set that we know are still alive after a couple of calls to removeStaleEntries.

if (Math.abs(estimatedSize - previousEstimate) > OLD_KEYS_THRESHOLD
|| estimatedSize >= (GLOBAL_HARD_LIMIT + GLOBAL_SOFT_LIMIT) / 2) {
if (Math.abs(estimatedSize - previousEstimate) > OLD_KEYS_THRESHOLD
|| estimatedSize >= (GLOBAL_HARD_LIMIT + GLOBAL_SOFT_LIMIT) / 2) {

if (estimatedSize >= GLOBAL_SOFT_LIMIT) {
// start proactively removing old content to keep growth in check
for (StoreKey oldKey : oldKeys) {
if (weakMap.remove(oldKey) != null) {
estimatedSize--;
if (estimatedSize >= GLOBAL_SOFT_LIMIT) {
// start proactively removing old content to keep growth in check
for (StoreKey oldKey : oldKeys) {
if (weakMap.remove(oldKey) != null) {
estimatedSize--;
}
}
oldKeys.clear();
} else {
// have any of the old previously sampled keys been collected?
oldKeys.removeIf(StoreKey::isStale);
}
oldKeys.clear();
} else {
// have any of the old previously sampled keys been collected?
oldKeys.removeIf(StoreKey::isStale);
}

int refill = OLD_KEYS_THRESHOLD - oldKeys.size();
if (refill > 0) {
// sample of keys at this time, don't need strict age ordering
for (StoreKey sampleKey : weakMap.keySet()) {
if (oldKeys.add(sampleKey) && --refill == 0) {
break;
int refill = OLD_KEYS_THRESHOLD - oldKeys.size();
if (refill > 0) {
// sample of keys at this time, don't need strict age ordering
for (StoreKey sampleKey : weakMap.keySet()) {
if (oldKeys.add(sampleKey) && --refill == 0) {
break;
}
}
}

previousEstimate = estimatedSize;
}

previousEstimate = estimatedSize;
return estimatedSize;
}

return estimatedSize;
}

/**
Expand Down
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ asm = "9.10.1"
junit-jupiter = "5.14.4"
junit-platform = "1.14.4"
assertj = "3.27.7"
spotless = "8.5.1"
spotbugs = "6.5.4"
spotbugs-annotations = "4.9.8"
axion-release = "1.21.1"
spotless = "8.8.0"
spotbugs = "6.5.8"
spotbugs-annotations = "4.10.2"
axion-release = "1.21.2"
nexus-publish = "2.0.0"
jmh-plugin = "0.7.3"

Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bafc141b619ad6350fd975fc903156dd5c151998cc8b058e8c1044ab5f7b031f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static datadog.instrument.utils.ClassLoaderKey.SYSTEM_CLASS_LOADER;

import datadog.instrument.utils.ClassLoaderKey.LookupKey;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
Expand Down Expand Up @@ -45,6 +46,7 @@ public abstract class ClassLoaderValue<V> {
private final Map<ClassLoaderKey, V> otherValues = new ConcurrentHashMap<>();

/** Register subclass instances for cleaning. */
@SuppressFBWarnings("CT_CONSTRUCTOR_THROW") // registerCleaner never sees partial instance
protected ClassLoaderValue() {
ClassLoaderKey.registerCleaner(otherValues::remove);
}
Expand Down
Loading