Skip to content

Commit b139e1d

Browse files
authored
Look up helper class bytes when they are needed (#7839)
We can save a bit of heap space by not keeping the class bytes in memory, we can easily look them up when needed.
1 parent 36c21d7 commit b139e1d

File tree

1 file changed

+38
-18
lines changed

1 file changed

+38
-18
lines changed

muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.Map;
2929
import java.util.Set;
3030
import java.util.concurrent.ConcurrentHashMap;
31+
import java.util.function.Supplier;
3132
import javax.annotation.Nullable;
3233
import net.bytebuddy.agent.builder.AgentBuilder.Transformer;
3334
import net.bytebuddy.description.type.TypeDescription;
@@ -88,7 +89,7 @@ Class<?> inject(ClassLoader classLoader, String className) {
8889
private final List<HelperResource> helperResources;
8990
@Nullable private final ClassLoader helpersSource;
9091
@Nullable private final Instrumentation instrumentation;
91-
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
92+
private final Map<String, Supplier<byte[]>> dynamicTypeMap = new LinkedHashMap<>();
9293

9394
private final Cache<ClassLoader, Boolean> injectedClassLoaders = Cache.weak();
9495
private final Cache<ClassLoader, Boolean> resourcesInjectedClassLoaders = Cache.weak();
@@ -108,7 +109,6 @@ public HelperInjector(
108109
String requestingName,
109110
List<String> helperClassNames,
110111
List<HelperResource> helperResources,
111-
// TODO can this be replaced with the context class loader?
112112
ClassLoader helpersSource,
113113
Instrumentation instrumentation) {
114114
this.requestingName = requestingName;
@@ -120,7 +120,9 @@ public HelperInjector(
120120
}
121121

122122
private HelperInjector(
123-
String requestingName, Map<String, byte[]> helperMap, Instrumentation instrumentation) {
123+
String requestingName,
124+
Map<String, Supplier<byte[]>> helperMap,
125+
Instrumentation instrumentation) {
124126
this.requestingName = requestingName;
125127

126128
this.helperClassNames = helperMap.keySet();
@@ -135,9 +137,9 @@ public static HelperInjector forDynamicTypes(
135137
String requestingName,
136138
Collection<DynamicType.Unloaded<?>> helpers,
137139
Instrumentation instrumentation) {
138-
Map<String, byte[]> bytes = new HashMap<>(helpers.size());
140+
Map<String, Supplier<byte[]>> bytes = new HashMap<>(helpers.size());
139141
for (DynamicType.Unloaded<?> helper : helpers) {
140-
bytes.put(helper.getTypeDescription().getName(), helper.getBytes());
142+
bytes.put(helper.getTypeDescription().getName(), helper::getBytes);
141143
}
142144
return new HelperInjector(requestingName, bytes, instrumentation);
143145
}
@@ -146,18 +148,27 @@ public static void setHelperInjectorListener(HelperInjectorListener listener) {
146148
helperInjectorListener = listener;
147149
}
148150

149-
private Map<String, byte[]> getHelperMap() throws IOException {
151+
private Map<String, Supplier<byte[]>> getHelperMap() {
150152
if (dynamicTypeMap.isEmpty()) {
151-
Map<String, byte[]> classnameToBytes = new LinkedHashMap<>();
152-
153-
ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(helpersSource);
153+
Map<String, Supplier<byte[]>> result = new LinkedHashMap<>();
154154

155155
for (String helperClassName : helperClassNames) {
156-
byte[] classBytes = locator.locate(helperClassName).resolve();
157-
classnameToBytes.put(helperClassName, classBytes);
156+
result.put(
157+
helperClassName,
158+
() -> {
159+
try (ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(helpersSource)) {
160+
return locator.locate(helperClassName).resolve();
161+
} catch (IOException exception) {
162+
if (logger.isLoggable(SEVERE)) {
163+
logger.log(
164+
SEVERE, "Failed to read {0}", new Object[] {helperClassName}, exception);
165+
}
166+
throw new IllegalStateException("Failed to read " + helperClassName, exception);
167+
}
168+
});
158169
}
159170

160-
return classnameToBytes;
171+
return result;
161172
} else {
162173
return dynamicTypeMap;
163174
}
@@ -248,10 +259,10 @@ private void injectHelperClasses(TypeDescription typeDescription, ClassLoader cl
248259
new Object[] {cl, helperClassNames});
249260
}
250261

251-
Map<String, byte[]> classnameToBytes = getHelperMap();
262+
Map<String, Supplier<byte[]>> classnameToBytes = getHelperMap();
252263
Map<String, HelperClassInjector> map =
253264
helperInjectors.computeIfAbsent(cl, (unused) -> new ConcurrentHashMap<>());
254-
for (Map.Entry<String, byte[]> entry : classnameToBytes.entrySet()) {
265+
for (Map.Entry<String, Supplier<byte[]>> entry : classnameToBytes.entrySet()) {
255266
// for boot loader we use a placeholder injector, we only need these classes to be
256267
// in the injected classes map to later tell which of the classes are injected
257268
HelperClassInjector injector =
@@ -280,9 +291,18 @@ private void injectHelperClasses(TypeDescription typeDescription, ClassLoader cl
280291
});
281292
}
282293

283-
private Map<String, Class<?>> injectBootstrapClassLoader(Map<String, byte[]> classnameToBytes)
294+
private static Map<String, byte[]> resolve(Map<String, Supplier<byte[]>> classes) {
295+
Map<String, byte[]> result = new LinkedHashMap<>();
296+
for (Map.Entry<String, Supplier<byte[]>> entry : classes.entrySet()) {
297+
result.put(entry.getKey(), entry.getValue().get());
298+
}
299+
return result;
300+
}
301+
302+
private Map<String, Class<?>> injectBootstrapClassLoader(Map<String, Supplier<byte[]>> inject)
284303
throws IOException {
285304

305+
Map<String, byte[]> classnameToBytes = resolve(inject);
286306
if (helperInjectorListener != null) {
287307
helperInjectorListener.onInjection(classnameToBytes);
288308
}
@@ -358,16 +378,16 @@ public static Class<?> loadHelperClass(ClassLoader classLoader, String className
358378
}
359379

360380
private static class HelperClassInjector {
361-
private final byte[] bytes;
381+
private final Supplier<byte[]> bytes;
362382

363-
HelperClassInjector(byte[] bytes) {
383+
HelperClassInjector(Supplier<byte[]> bytes) {
364384
this.bytes = bytes;
365385
}
366386

367387
Class<?> inject(ClassLoader classLoader, String className) {
368388
Map<String, Class<?>> result =
369389
new ClassInjector.UsingReflection(classLoader)
370-
.injectRaw(Collections.singletonMap(className, bytes));
390+
.injectRaw(Collections.singletonMap(className, bytes.get()));
371391
return result.get(className);
372392
}
373393
}

0 commit comments

Comments
 (0)