This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 74c502234 [type:fix] fix memory leak (#4486)
74c502234 is described below
commit 74c502234b2aa933738c57c7aaa6dc996ad843df
Author: moremind <[email protected]>
AuthorDate: Fri Mar 17 16:33:21 2023 +0800
[type:fix] fix memory leak (#4486)
* [type:fix] fix memory leak
* [type:fix] fix memory leak
* [type:fix] fix memory leak
* [type:fix] fix memory leak
* fix ci
* add match rule cache
* fix ci
* fix ci
* fix ci
* fix ci
---
.../src/main/resources/application.yml | 3 +-
.../shenyu/common/cache/WindowTinyLFUMap.java | 59 +++++++++++++++++-----
.../apache/shenyu/common/config/ShenyuConfig.java | 45 +++++++++++++----
.../apache/shenyu/common/constant/Constants.java | 2 +-
.../shenyu/plugin/base/AbstractShenyuPlugin.java | 10 ++--
.../shenyu/plugin/base/cache/MatchDataCache.java | 12 +++--
.../shenyu/plugin/base/cache/MetaDataCache.java | 5 +-
.../plugin/base/cache/MatchDataCacheTest.java | 45 ++++++++++++++---
8 files changed, 135 insertions(+), 46 deletions(-)
diff --git a/shenyu-bootstrap/src/main/resources/application.yml
b/shenyu-bootstrap/src/main/resources/application.yml
index f0cf675d0..b120d8430 100644
--- a/shenyu-bootstrap/src/main/resources/application.yml
+++ b/shenyu-bootstrap/src/main/resources/application.yml
@@ -69,7 +69,8 @@ shenyu:
matchCache:
selector:
selectorEnabled: false
- maxSelectorFreeMemory: 256 # 256MB
+ initialCapacity: 10000 # initial capacity in cache
+ maximumSize: 10000 # max size in cache
rule:
initialCapacity: 10000 # initial capacity in cache
maximumSize: 10000 # max size in cache
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
index 27114aaf4..cafe5a7af 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/cache/WindowTinyLFUMap.java
@@ -22,10 +22,18 @@ import com.github.benmanes.caffeine.cache.Caffeine;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Serializable;
+import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+/**
+ * this cache is provided by caffeine, the cache has two implements including
weak-key cache and regular cache.<br>
+ * the weak-key cache applies to scenarios where objects can be collected.
when memory lock causes full gc, the weakKey will collect by gc.<br>
+ * the regular cache applies to immutable cache object data, and the user
determines the cache size.<br>
+ * about the weak-key cache and regular cache, please refer to:
+ * <a href="https://github.com/ben-manes/caffeine/issues/776">caffeine cache
ISSUES #776</a>
+ */
@ThreadSafe
public class WindowTinyLFUMap<K, V> extends AbstractMap<K, V> implements
Serializable {
@@ -33,24 +41,51 @@ public class WindowTinyLFUMap<K, V> extends AbstractMap<K,
V> implements Seriali
private final Cache<K, V> cache;
- public WindowTinyLFUMap(final int initialCapacity, final long maximumSize)
{
- this.cache = Caffeine.newBuilder()
- .initialCapacity(initialCapacity)
- .maximumSize(maximumSize)
- .build();
+ /**
+ * initial caffeine cache.
+ * when weakKey is true, create weakKeys cache, please refer to:
{@linkplain WeakReference},
+ * weak reference make cache be memory-safe. however, the weakKey is
false, caffeine eject cache by strategy.
+ *
+ * @param initialCapacity initial capacity
+ * @param maximumSize maximum size
+ * @param weakKey weak key
+ */
+ public WindowTinyLFUMap(final int initialCapacity, final long maximumSize,
final Boolean weakKey) {
+ if (Boolean.TRUE.equals(weakKey)) {
+ this.cache = Caffeine.newBuilder()
+ .weakKeys()
+ .initialCapacity(initialCapacity)
+ .maximumSize(maximumSize)
+ .build();
+ } else {
+ this.cache = Caffeine.newBuilder()
+ .initialCapacity(initialCapacity)
+ .maximumSize(maximumSize)
+ .build();
+ }
}
- public WindowTinyLFUMap(final int initialSize, final long
expireAfterWrite, final long maximumSize) {
- this.cache = Caffeine.newBuilder()
- .initialCapacity(initialSize)
- .expireAfterWrite(expireAfterWrite, TimeUnit.MILLISECONDS)
- .maximumSize(maximumSize)
- .build();
+ public WindowTinyLFUMap(final int initialSize, final long
expireAfterWrite, final long maximumSize, final Boolean weakKey) {
+ if (Boolean.TRUE.equals(weakKey)) {
+ this.cache = Caffeine.newBuilder()
+ .weakKeys()
+ .initialCapacity(initialSize)
+ .expireAfterWrite(expireAfterWrite, TimeUnit.MILLISECONDS)
+ .maximumSize(maximumSize)
+ .build();
+ } else {
+ this.cache = Caffeine.newBuilder()
+ .initialCapacity(initialSize)
+ .expireAfterWrite(expireAfterWrite, TimeUnit.MILLISECONDS)
+ .maximumSize(maximumSize)
+ .build();
+ }
+
}
@Override
public V put(final K key, final V value) {
- V v = cache.getIfPresent(value);
+ V v = cache.getIfPresent(key);
cache.put(key, value);
return v;
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
index e47509e05..42401f124 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/config/ShenyuConfig.java
@@ -581,9 +581,14 @@ public class ShenyuConfig {
private boolean selectorEnabled;
/**
- * Max free memory, unit mb.
+ * initialCapacity.
*/
- private Integer maxSelectorFreeMemory = 256;
+ private int initialCapacity = 10000;
+
+ /**
+ * maximumSize.
+ */
+ private long maximumSize = 10000L;
/**
* Get selector cache enabled.
@@ -602,23 +607,41 @@ public class ShenyuConfig {
public void setSelectorEnabled(final boolean selectorEnabled) {
this.selectorEnabled = selectorEnabled;
}
-
+
+ /**
+ * get initialCapacity.
+ *
+ * @return initialCapacity
+ */
+ public int getInitialCapacity() {
+ return initialCapacity;
+ }
+
/**
- * Gets maxFreeMemory.
+ * set initialCapacity.
*
- * @return the maxFreeMemory
+ * @param initialCapacity initialCapacity
*/
- public Integer getMaxSelectorFreeMemory() {
- return maxSelectorFreeMemory;
+ public void setInitialCapacity(final int initialCapacity) {
+ this.initialCapacity = initialCapacity;
}
/**
- * Sets maxFreeMemory.
+ * get maximumSize.
*
- * @param maxSelectorFreeMemory the maxFreeMemory
+ * @return maximumSize
*/
- public void setMaxSelectorFreeMemory(final Integer
maxSelectorFreeMemory) {
- this.maxSelectorFreeMemory = maxSelectorFreeMemory;
+ public long getMaximumSize() {
+ return maximumSize;
+ }
+
+ /**
+ * set maximumSize.
+ *
+ * @param maximumSize maximumSize
+ */
+ public void setMaximumSize(final long maximumSize) {
+ this.maximumSize = maximumSize;
}
}
diff --git
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
index eb93692a2..2a871b19e 100644
---
a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
+++
b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/Constants.java
@@ -666,7 +666,7 @@ public interface Constants {
* user can use the specify-domian to replace of upstream url of the
divide plugin.
*/
String SPECIFY_DOMAIN = "specify-domain";
-
+
/**
* The maximum free memory reserved by the blocking queue for the JVM.
*/
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
index 561377b2d..c3bac8466 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/AbstractShenyuPlugin.java
@@ -176,23 +176,21 @@ public abstract class AbstractShenyuPlugin implements
ShenyuPlugin {
|| (Objects.nonNull(selectorData.getMatchRestful()) &&
selectorData.getMatchRestful())) {
return;
}
+ int initialCapacity =
matchCacheConfig.getSelector().getInitialCapacity();
+ long maximumSize = matchCacheConfig.getSelector().getMaximumSize();
if (StringUtils.isBlank(selectorData.getId())) {
- MatchDataCache.getInstance().cacheSelectorData(path, selectorData,
getSelectorMaxFreeMemory());
+ MatchDataCache.getInstance().cacheSelectorData(path, selectorData,
initialCapacity, maximumSize);
return;
}
List<ConditionData> conditionList = selectorData.getConditionList();
if (CollectionUtils.isNotEmpty(conditionList)) {
boolean isUriCondition = conditionList.stream().allMatch(v ->
URI_CONDITION_TYPE.equals(v.getParamType()));
if (isUriCondition) {
- MatchDataCache.getInstance().cacheSelectorData(path,
selectorData, getSelectorMaxFreeMemory());
+ MatchDataCache.getInstance().cacheSelectorData(path,
selectorData, initialCapacity, maximumSize);
}
}
}
- private Integer getSelectorMaxFreeMemory() {
- return matchCacheConfig.getSelector().getMaxSelectorFreeMemory() *
1024 * 1024;
- }
-
private SelectorData obtainSelectorDataCacheIfEnabled(final String path) {
return matchCacheConfig.getSelector().getSelectorEnabled() ?
MatchDataCache.getInstance().obtainSelectorData(named(), path) : null;
}
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
index 61a6d96d5..0294474fa 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MatchDataCache.java
@@ -18,7 +18,6 @@
package org.apache.shenyu.plugin.base.cache;
import com.google.common.collect.Maps;
-import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
import org.apache.shenyu.common.cache.WindowTinyLFUMap;
import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
@@ -80,10 +79,12 @@ public final class MatchDataCache {
*
* @param path the path
* @param selectorData the selector data
- * @param maxMemory the max memory
+ * @param initialCapacity initialCapacity
+ * @param maximumSize maximumSize
*/
- public void cacheSelectorData(final String path, final SelectorData
selectorData, final Integer maxMemory) {
- MapUtils.computeIfAbsent(SELECTOR_DATA_MAP,
selectorData.getPluginName(), map -> new
MemorySafeWindowTinyLFUMap<>(maxMemory, 1 << 16)).put(path, selectorData);
+ public void cacheSelectorData(final String path, final SelectorData
selectorData, final int initialCapacity, final long maximumSize) {
+ MapUtils.computeIfAbsent(SELECTOR_DATA_MAP,
selectorData.getPluginName(), map ->
+ new WindowTinyLFUMap<>(initialCapacity, maximumSize,
Boolean.TRUE)).put(path, selectorData);
}
/**
@@ -107,7 +108,8 @@ public final class MatchDataCache {
* @param maximumSize maximum size
*/
public void cacheRuleData(final String path, final RuleData ruleData,
final int initialCapacity, final long maximumSize) {
- MapUtils.computeIfAbsent(RULE_DATA_MAP, ruleData.getPluginName(), map
-> new WindowTinyLFUMap<>(initialCapacity, maximumSize)).put(path, ruleData);
+ MapUtils.computeIfAbsent(RULE_DATA_MAP, ruleData.getPluginName(), map
->
+ new WindowTinyLFUMap<>(initialCapacity, maximumSize,
Boolean.TRUE)).put(path, ruleData);
}
/**
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
index 447f37653..4d6e151ff 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
@@ -18,8 +18,7 @@
package org.apache.shenyu.plugin.base.cache;
import com.google.common.collect.Maps;
-import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
-import org.apache.shenyu.common.constant.Constants;
+import org.apache.shenyu.common.cache.WindowTinyLFUMap;
import org.apache.shenyu.common.dto.MetaData;
import org.apache.shenyu.plugin.base.utils.PathMatchUtils;
@@ -45,7 +44,7 @@ public final class MetaDataCache {
*/
private static final ConcurrentMap<String, MetaData> META_DATA_MAP =
Maps.newConcurrentMap();
- private static final MemorySafeWindowTinyLFUMap<String, MetaData> CACHE =
new MemorySafeWindowTinyLFUMap<>(Constants.THE_256_MB, 1 << 16);
+ private static final WindowTinyLFUMap<String, MetaData> CACHE = new
WindowTinyLFUMap<>(1 << 16, Integer.MAX_VALUE, Boolean.FALSE);
/**
* pathPattern -> path.
diff --git
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
index 086d0f0d9..b5b49dae5 100644
---
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
+++
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MatchDataCacheTest.java
@@ -17,7 +17,8 @@
package org.apache.shenyu.plugin.base.cache;
-import org.apache.shenyu.common.cache.MemorySafeWindowTinyLFUMap;
+import org.apache.shenyu.common.cache.WindowTinyLFUMap;
+import org.apache.shenyu.common.dto.RuleData;
import org.apache.shenyu.common.dto.SelectorData;
import org.junit.jupiter.api.Test;
@@ -41,8 +42,8 @@ public final class MatchDataCacheTest {
@Test
public void testCacheSelectorData() throws NoSuchFieldException,
IllegalAccessException {
SelectorData firstCachedSelectorData =
SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- MatchDataCache.getInstance().cacheSelectorData(path1,
firstCachedSelectorData, 5 * 1024);
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
SelectorData>> selectorMap = getFieldByName(selectorMapStr);
+ MatchDataCache.getInstance().cacheSelectorData(path1,
firstCachedSelectorData, 100, 100);
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, SelectorData>>
selectorMap = getFieldByName(selectorMapStr);
assertEquals(firstCachedSelectorData,
selectorMap.get(mockPluginName1).get(path1));
selectorMap.clear();
}
@@ -50,8 +51,8 @@ public final class MatchDataCacheTest {
@Test
public void testObtainSelectorData() throws NoSuchFieldException,
IllegalAccessException {
SelectorData firstSelectorData =
SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
SelectorData>> selectorMap = getFieldByName(selectorMapStr);
- selectorMap.put(mockPluginName1, new MemorySafeWindowTinyLFUMap<>(5 *
1024, 16));
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, SelectorData>>
selectorMap = getFieldByName(selectorMapStr);
+ selectorMap.put(mockPluginName1, new WindowTinyLFUMap<>(100, 100,
Boolean.TRUE));
selectorMap.get(mockPluginName1).put(path1, firstSelectorData);
SelectorData firstSelectorDataCache =
MatchDataCache.getInstance().obtainSelectorData(mockPluginName1, path1);
assertEquals(firstSelectorData, firstSelectorDataCache);
@@ -61,9 +62,9 @@ public final class MatchDataCacheTest {
@Test
public void testRemoveSelectorData() throws NoSuchFieldException,
IllegalAccessException {
SelectorData firstCachedSelectorData =
SelectorData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
- MatchDataCache.getInstance().cacheSelectorData(path1,
firstCachedSelectorData, 5 * 1024);
+ MatchDataCache.getInstance().cacheSelectorData(path1,
firstCachedSelectorData, 100, 100);
MatchDataCache.getInstance().removeSelectorData(firstCachedSelectorData.getPluginName());
- ConcurrentHashMap<String, MemorySafeWindowTinyLFUMap<String,
SelectorData>> selectorMap = getFieldByName(selectorMapStr);
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, SelectorData>>
selectorMap = getFieldByName(selectorMapStr);
assertNull(selectorMap.get(mockPluginName1));
selectorMap.clear();
}
@@ -75,4 +76,34 @@ public final class MatchDataCacheTest {
pluginMapField.setAccessible(true);
return (ConcurrentHashMap) pluginMapField.get(matchDataCache);
}
+
+ @Test
+ public void testCacheRuleData() throws NoSuchFieldException,
IllegalAccessException {
+ RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ MatchDataCache.getInstance().cacheRuleData(path1, cacheRuleData, 100,
100);
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, RuleData>> ruleMap
= getFieldByName(ruleMapStr);
+ assertEquals(cacheRuleData, ruleMap.get(mockPluginName1).get(path1));
+ ruleMap.clear();
+ }
+
+ @Test
+ public void testObtainRuleData() throws NoSuchFieldException,
IllegalAccessException {
+ RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, RuleData>> ruleMap
= getFieldByName(ruleMapStr);
+ ruleMap.put(mockPluginName1, new WindowTinyLFUMap<>(100, 100,
Boolean.TRUE));
+ ruleMap.get(mockPluginName1).put(path1, cacheRuleData);
+ RuleData firstRuleDataCache =
MatchDataCache.getInstance().obtainRuleData(mockPluginName1, path1);
+ assertEquals(cacheRuleData, firstRuleDataCache);
+ ruleMap.clear();
+ }
+
+ @Test
+ public void testRemoveRuleData() throws NoSuchFieldException,
IllegalAccessException {
+ RuleData cacheRuleData =
RuleData.builder().id("1").pluginName(mockPluginName1).sort(1).build();
+ MatchDataCache.getInstance().cacheRuleData(path1, cacheRuleData, 100,
100);
+
MatchDataCache.getInstance().removeRuleData(cacheRuleData.getPluginName());
+ ConcurrentHashMap<String, WindowTinyLFUMap<String, RuleData>> ruleMap
= getFieldByName(ruleMapStr);
+ assertNull(ruleMap.get(mockPluginName1));
+ ruleMap.clear();
+ }
}