Skip to content

Commit eac05b1

Browse files
authored
fix: Use WeakHashMap for caching proxy classes (#2260)
1 parent f99533c commit eac05b1

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

src/main/java/io/appium/java_client/proxy/Helpers.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
import java.lang.reflect.Method;
3232
import java.util.Collection;
3333
import java.util.Collections;
34+
import java.util.Map;
3435
import java.util.Set;
35-
import java.util.concurrent.ConcurrentHashMap;
36-
import java.util.concurrent.ConcurrentMap;
36+
import java.util.WeakHashMap;
3737
import java.util.stream.Collectors;
3838
import java.util.stream.Stream;
3939

@@ -50,7 +50,8 @@ public class Helpers {
5050
// the performance and to avoid extensive memory usage for our case, where
5151
// the amount of instrumented proxy classes we create is low in comparison to the amount
5252
// of proxy instances.
53-
private static final ConcurrentMap<ProxyClassSignature, Class<?>> CACHED_PROXY_CLASSES = new ConcurrentHashMap<>();
53+
private static final Map<ProxyClassSignature, Class<?>> CACHED_PROXY_CLASSES =
54+
Collections.synchronizedMap(new WeakHashMap<>());
5455

5556
private Helpers() {
5657
}

src/test/java/io/appium/java_client/pagefactory_tests/widget/tests/combined/CombinedWidgetTest.java

+38
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,29 @@
1212
import org.openqa.selenium.WebDriver;
1313
import org.openqa.selenium.WebElement;
1414

15+
import java.lang.reflect.Field;
1516
import java.util.ArrayList;
1617
import java.util.List;
18+
import java.util.Map;
1719
import java.util.stream.Stream;
1820

1921
import static java.util.stream.Collectors.toList;
2022
import static org.hamcrest.MatcherAssert.assertThat;
2123
import static org.hamcrest.Matchers.contains;
2224
import static org.hamcrest.Matchers.equalTo;
25+
import static org.hamcrest.Matchers.lessThan;
2326
import static org.openqa.selenium.support.PageFactory.initElements;
2427

2528

2629
@SuppressWarnings({"unchecked", "unused"})
2730
public class CombinedWidgetTest {
2831

32+
/**
33+
* Based on how many Proxy Classes are created during this test class,
34+
* this number is used to determine if the cache is being purged correctly between tests.
35+
*/
36+
private static final int THRESHOLD_SIZE = 50;
37+
2938
/**
3039
* Test data generation.
3140
*
@@ -57,6 +66,7 @@ public static Stream<Arguments> data() {
5766
@ParameterizedTest
5867
@MethodSource("data")
5968
void checkThatWidgetsAreCreatedCorrectly(AbstractApp app, WebDriver driver, Class<?> widgetClass) {
69+
assertProxyClassCacheGrowth();
6070
initElements(new AppiumFieldDecorator(driver), app);
6171
assertThat("Expected widget class was " + widgetClass.getName(),
6272
app.getWidget().getSubWidget().getSelfReference().getClass(),
@@ -161,4 +171,32 @@ public List<PartiallyCombinedWidget> getWidgets() {
161171
return multipleWidgets;
162172
}
163173
}
174+
175+
176+
/**
177+
* Assert proxy class cache growth for this test class.
178+
* The (@link io.appium.java_client.proxy.Helpers#CACHED_PROXY_CLASSES) should be populated during these tests.
179+
* Prior to the Caching issue being resolved
180+
* - the CACHED_PROXY_CLASSES would grow indefinitely, resulting in an Out Of Memory exception.
181+
* - this ParameterizedTest would have the CACHED_PROXY_CLASSES grow to 266 entries.
182+
*/
183+
private void assertProxyClassCacheGrowth() {
184+
System.gc(); //Trying to force a collection for more accurate check numbers
185+
assertThat(
186+
"Proxy Class Cache threshold is " + THRESHOLD_SIZE,
187+
getCachedProxyClassesSize(),
188+
lessThan(THRESHOLD_SIZE)
189+
);
190+
}
191+
192+
private int getCachedProxyClassesSize() {
193+
try {
194+
Field cpc = Class.forName("io.appium.java_client.proxy.Helpers").getDeclaredField("CACHED_PROXY_CLASSES");
195+
cpc.setAccessible(true);
196+
Map<?, ?> cachedProxyClasses = (Map<?, ?>) cpc.get(null);
197+
return cachedProxyClasses.size();
198+
} catch (NoSuchFieldException | ClassNotFoundException | IllegalAccessException e) {
199+
throw new RuntimeException(e);
200+
}
201+
}
164202
}

0 commit comments

Comments
 (0)