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();
+    }
 }

Reply via email to