Skip to content
This repository was archived by the owner on Sep 2, 2022. It is now read-only.
/ jdk16u Public archive

Commit b6db9a5

Browse files
committed
8198540: Dynalink leaks memory when generating type converters
Backport-of: 8f4c15f6417e471b372f74460fa94b9d84c4811d
1 parent 988dfa8 commit b6db9a5

File tree

5 files changed

+552
-234
lines changed

5 files changed

+552
-234
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package jdk.dynalink;
27+
28+
import java.lang.invoke.MethodHandles;
29+
import java.lang.invoke.VarHandle;
30+
import java.security.AccessControlContext;
31+
import java.security.AccessController;
32+
import java.security.PrivilegedAction;
33+
import java.util.Map;
34+
import java.util.Objects;
35+
import java.util.function.BiFunction;
36+
import java.util.function.Function;
37+
import jdk.dynalink.internal.AccessControlContextFactory;
38+
39+
import static jdk.dynalink.internal.InternalTypeUtilities.canReferenceDirectly;
40+
41+
/**
42+
* Similar to ClassValue, but lazily associates a computed value with
43+
* (potentially) every pair of types.
44+
* @param <T> the value to associate with pairs of types.
45+
*/
46+
final class BiClassValue<T> {
47+
/**
48+
* Creates a new BiClassValue that uses the specified binary function to
49+
* lazily compute the values.
50+
* @param compute the binary function to compute the values. Ordinarily, it
51+
* is invoked at most once for any pair of values. However,
52+
* it is possible for it to be invoked concurrently. It can
53+
* even be invoked concurrently multiple times for the same
54+
* arguments under contention. In that case, it is undefined
55+
* which of the computed values will be retained therefore
56+
* returning semantically equivalent values is strongly
57+
* recommended; the function should ideally be pure.
58+
* Additionally, if the pair of types passed as parameters
59+
* are from unrelated class loaders, the computed value is
60+
* not cached at all and the function might be reinvoked
61+
* with the same parameters in the future. Finally, a null
62+
* return value is allowed, but not cached.
63+
* @param <T> the type of the values
64+
* @return a new BiClassValue that computes the values using the passed
65+
* function.
66+
*/
67+
static <T> BiClassValue<T> computing(final BiFunction<Class<?>, Class<?>, T> compute) {
68+
return new BiClassValue<>(compute);
69+
}
70+
71+
/**
72+
* A type-specific map that stores the values specific to pairs of types
73+
* which include its class in one of the positions of the pair. Internally,
74+
* it uses at most two maps named "forward" and "reverse". A BiClassValues
75+
* for class C1 can store values for (C1, Cy) in its forward map as well
76+
* as values for (Cx, C1) in its reverse map. The reason for this scheme
77+
* is to avoid creating unwanted strong references from a parent class
78+
* loader to a child class loader. If for a pair of classes (C1, C2)
79+
* either C1 and C2 are in the same class loader, or C2 is in parent of C1,
80+
* or C2 is a system class, forward map of C1's BiClassValues is used for
81+
* storing the computed value. If C1 is in parent of C2, or C1 is a system
82+
* class, reverse map of C2's BiClassValues is used for storing. If the
83+
* class loaders are unrelated, the computed value is not cached and will
84+
* be recomputed on every evaluation.
85+
* NOTE that while every instance of this class is type-specific, it does
86+
* not store a reference to the type Class object itself. BiClassValuesRoot
87+
* creates the association from a type Class object to its BiClassValues'.
88+
* @param <T> the type of the values
89+
*/
90+
private final static class BiClassValues<T> {
91+
// These will be used for compareAndExchange on forward and reverse fields.
92+
private static final VarHandle FORWARD;
93+
private static final VarHandle REVERSE;
94+
static {
95+
final MethodHandles.Lookup lookup = MethodHandles.lookup();
96+
try {
97+
FORWARD = lookup.findVarHandle(BiClassValues.class, "forward", Map.class);
98+
REVERSE = lookup.findVarHandle(BiClassValues.class, "reverse", Map.class);
99+
} catch (NoSuchFieldException | IllegalAccessException e) {
100+
throw new AssertionError(e);
101+
}
102+
}
103+
104+
private Map<Class<?>, T> forward = Map.of();
105+
private Map<Class<?>, T> reverse = Map.of();
106+
107+
T getForwardValue(final Class<?> c) {
108+
return forward.get(c);
109+
}
110+
111+
T getReverseValue(final Class<?> c) {
112+
return reverse.get(c);
113+
}
114+
115+
private T compute(final VarHandle mapHandle, final Class<?> c, final Function<Class<?>, T> compute) {
116+
@SuppressWarnings("unchecked")
117+
Map<Class<?>, T> map = (Map<Class<?>, T>) mapHandle.getVolatile(this);
118+
T value;
119+
T newValue = null;
120+
while ((value = map.get(c)) == null) {
121+
if (newValue == null) {
122+
newValue = compute.apply(c);
123+
if (newValue == null) {
124+
break;
125+
}
126+
}
127+
@SuppressWarnings({"unchecked", "rawtypes"})
128+
final Map.Entry<Class<?>, T>[] entries = map.entrySet().toArray(new Map.Entry[map.size() + 1]);
129+
entries[map.size()] = Map.entry(c, newValue);
130+
final var newMap = Map.ofEntries(entries);
131+
@SuppressWarnings("unchecked")
132+
final var witness = (Map<Class<?>, T>) mapHandle.compareAndExchange(this, map, newMap);
133+
if (witness == map) {
134+
value = newValue;
135+
break;
136+
}
137+
map = witness;
138+
}
139+
return value;
140+
}
141+
142+
T computeForward(final Class<?> c, Function<Class<?>, T> compute) {
143+
return compute(FORWARD, c, compute);
144+
}
145+
146+
T computeReverse(final Class<?> c, Function<Class<?>, T> compute) {
147+
return compute(REVERSE, c, compute);
148+
}
149+
}
150+
151+
// A named class used for "root" field so it can be static so it doesn't
152+
// gain a synthetic this$0 reference as that'd cause a memory leak through
153+
// unwanted anchoring to a GC root when used with system classes.
154+
private static final class BiClassValuesRoot<T> extends ClassValue<BiClassValues<T>> {
155+
@Override protected BiClassValues<T> computeValue(Class<?> type) {
156+
return new BiClassValues<>();
157+
}
158+
}
159+
160+
private enum RetentionDirection {
161+
FORWARD,
162+
REVERSE,
163+
NEITHER
164+
}
165+
166+
private final BiClassValuesRoot<T> root = new BiClassValuesRoot<>();
167+
private final BiFunction<Class<?>, Class<?>, T> compute;
168+
169+
private BiClassValue(final BiFunction<Class<?>, Class<?>, T> compute) {
170+
this.compute = Objects.requireNonNull(compute);
171+
}
172+
173+
final T get(final Class<?> c1, final Class<?> c2) {
174+
// Most likely case: it is in the forward map of c1's BiClassValues
175+
final BiClassValues<T> cv1 = root.get(c1);
176+
final T v1 = cv1.getForwardValue(c2);
177+
if (v1 != null) {
178+
return v1;
179+
}
180+
181+
// Next likely case: it is in the reverse map of c2's BiClassValues
182+
final BiClassValues<T> cv2 = root.get(c2);
183+
final T v2 = cv2.getReverseValue(c1);
184+
if (v2 != null) {
185+
return v2;
186+
}
187+
188+
// Value is uncached, compute it and cache if possible.
189+
switch (getRetentionDirection(c1, c2)) {
190+
case FORWARD:
191+
// loader of c1 can see loader of c2, store value for (c1, c2) in cv1's forward map
192+
return cv1.computeForward(c2, cy -> compute.apply(c1, cy));
193+
case REVERSE:
194+
// loader of c2 can see loader of c1, store value for (c1, c2) in cv2's reverse map
195+
return cv2.computeReverse(c1, cx -> compute.apply(cx, c2));
196+
case NEITHER:
197+
// Class loaders are unrelated; compute and return uncached.
198+
return compute.apply(c1, c2);
199+
default:
200+
throw new AssertionError(); // enum values exhausted
201+
}
202+
}
203+
204+
private static final AccessControlContext GET_CLASS_LOADER_CONTEXT =
205+
AccessControlContextFactory.createAccessControlContext("getClassLoader");
206+
207+
private static RetentionDirection getRetentionDirection(Class<?> from, Class<?> to) {
208+
return AccessController.doPrivileged((PrivilegedAction<RetentionDirection>) () -> {
209+
final ClassLoader cl1 = from.getClassLoader();
210+
final ClassLoader cl2 = to.getClassLoader();
211+
if (canReferenceDirectly(cl1, cl2)) {
212+
return RetentionDirection.FORWARD;
213+
} else if (canReferenceDirectly(cl2, cl1)) {
214+
return RetentionDirection.REVERSE;
215+
}
216+
return RetentionDirection.NEITHER;
217+
}, GET_CLASS_LOADER_CONTEXT);
218+
}
219+
}

src/jdk.dynalink/share/classes/jdk/dynalink/ClassMap.java

-157
This file was deleted.

0 commit comments

Comments
 (0)