WeakReference的。当只剩下WeakReferences时,不会删除对象

时间:2016-09-29 08:10:33

标签: java garbage-collection weak-references jvm-hotspot weakhashmap

在我们的系统中,我们将客户端会话作为类Session。

此类的哈希代码在历史上是可变的 - 在创建时为0,在某些时间点更改为用户ID。

系统中存在两个会话管理器。

  1. 客户端会话管理器 - 保持活动客户端会话是客户端。在这个经理里面是简单的ConcurrentHashMap<长,会话>其中key是用户ID。
  2. 内存会话管理器 - 保留GC尚未收集的会话。 里面有WeakHashMap<会话,长> (这里的值是用户ID)
  3. 当客户端连接并登录下一个流程时:

    1. 创建了客户端会话。
    2. 会话进入内存会话管理器(此时哈希码为0,因此将它们放入单个存储桶中)
    3. 客户端已登录,会话使用正确的哈希码放入客户端会话管理器。
    4. 关闭客户端会话并从客户端会话管理器中删除。
    5. 结果收集了Session对象的所有强引用后,必须从WeakHashMap中删除它。 (当WeakHashMap中的条目将自动删除  它的关键不再是普通用途。更准确地说,存在一个  给定密钥的映射不会阻止密钥被丢弃  垃圾收集器,即最终确定,最终确定,然后回收。) 但出于某种原因,他们留在那里。 代码如下:

      public class MemorySessionManager implements Runnable {
              private static final Logger logger = LoggerFactory.getLogger("checker");
      
              private Map<Session, Long> sessions;
      
              public MemorySessionManager() {
                  this.sessions = new WeakHashMap<>();
              }
      
              public synchronized void addSession(Session sess) {
                  sessions.put(sess, sess.getId());
              }
      
              public void run() {
                  Set<Session> sessionsToCheck = new HashSet<>();
                  synchronized (this) {
                      sessionsToCheck.addAll(sessions.keySet());
                  }
                  for (Session sess : sessionsToCheck) {
                      logger.warn("MemorySessionManager: Is still here: " + sess);
                  }
              }
         }
      

      简化的程序流程和会话类(没有无用的信息)。

      import java.util.Map;
      import java.util.concurrent.ConcurrentHashMap;
      
      public class ClientSessionManager {
      
          private Map<Long, Session> sessions;
      
          public ClientSessionManager() {
             this.sessions = new ConcurrentHashMap<>();
          }
      
          public void addSession(Session session) {
             sessions.put(session.getUserId(), session);
          }
      
          public Session removeSession(long code) {
             return sessions.remove(code);
          }
      }
      
      public class Session {
      
          private long userId;
      
          public void setUserId(long userId) {
              this.userId = userId;
          }
      
          public long getUserId() {
              return userId;
          }
      
          @Override
          public boolean equals(Object o) {
              if (this == o)
                 return true;
              if (o == null || getClass() != o.getClass())
                 return false;
      
              Session session = (Session) o;
      
              return userId == session.userId;
      
         }
      
         @Override
         public int hashCode() {
             return (int) (userId ^ (userId >>> 32));
         }
      }
      
      public class Process {
      
          private static final ClientSessionManager CLIENT_SESSION_MANAGER = new ClientSessionManager();
          private static final MemorySessionManager MEMORY_SESSION_MANAGER = new MemorySessionManager();
      
          /**
           * Login user, create its session and register session into appropriate managers.
           * 
           * @param userId id of user
           */
          public void login(long userId) {
              Session session = new Session();
              MEMORY_SESSION_MANAGER.addSession(session);
              session.setUserId(userId);
              CLIENT_SESSION_MANAGER.addSession(session);
          }
      
          /**
           * Close session of user, remove it from session manager
           * 
           * @param userId id of user
           */
          public void close(long userId) {
              CLIENT_SESSION_MANAGER.removeSession(userId);
          }
      }
      

      在内存中剩余的大量GC循环之后。这是登录GC(G1,混合)

      [GC pause (G1 Evacuation Pause) (mixed)
      Desired survivor size 6815744 bytes, new threshold 4 (max 15)
      - age   1:     236400 bytes,     236400 total
      - age   2:     350240 bytes,     586640 total
      - age   3:    3329024 bytes,    3915664 total
      - age   4:    2926992 bytes,    6842656 total
      , 0.0559520 secs]
         [Parallel Time: 51.9 ms, GC Workers: 2]
            [GC Worker Start (ms): Min: 73278041.7, Avg: 73278041.8, Max: 73278042.0, Diff: 0.2]
            [Ext Root Scanning (ms): Min: 3.2, Avg: 3.4, Max: 3.7, Diff: 0.5, Sum: 6.9]
            [Update RS (ms): Min: 15.0, Avg: 15.0, Max: 15.0, Diff: 0.0, Sum: 30.1]
               [Processed Buffers: Min: 59, Avg: 65.0, Max: 71, Diff: 12, Sum: 130]
            [Scan RS (ms): Min: 13.6, Avg: 14.3, Max: 15.1, Diff: 1.5, Sum: 28.7]
            [Code Root Scanning (ms): Min: 0.4, Avg: 1.2, Max: 2.0, Diff: 1.5, Sum: 2.4]
            [Object Copy (ms): Min: 17.5, Avg: 17.7, Max: 17.8, Diff: 0.3, Sum: 35.4]
            [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
               [Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 2]
            [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
            [GC Worker Total (ms): Min: 51.6, Avg: 51.7, Max: 51.8, Diff: 0.2, Sum: 103.4]
            [GC Worker End (ms): Min: 73278093.6, Avg: 73278093.6, Max: 73278093.6, Diff: 0.0]
         [Code Root Fixup: 0.7 ms]
         [Code Root Purge: 0.0 ms]
         [Clear CT: 0.4 ms]
         [Other: 2.9 ms]
            [Choose CSet: 0.6 ms]
            [Ref Proc: 0.3 ms]
            [Ref Enq: 0.0 ms]
            [Redirty Cards: 0.3 ms]
            [Humongous Register: 0.1 ms]
            [Humongous Reclaim: 0.0 ms]
            [Free CSet: 0.9 ms]
         [Eden: 95.0M(95.0M)->0.0B(1223.0M) Survivors: 7168.0K->5120.0K Heap: 512.0M(2048.0M)->363.5M(2048.0M)]
       [Times: user=0.11 sys=0.00, real=0.06 secs]
      2016-09-28T10:40:00.815+0000: 73288.384: [GC pause (G1 Evacuation Pause) (young)
      Desired survivor size 80740352 bytes, new threshold 15 (max 15)
      - age   1:    1096960 bytes,    1096960 total
      - age   2:     220208 bytes,    1317168 total
      - age   3:     349352 bytes,    1666520 total
      - age   4:    3325200 bytes,    4991720 tota
      

      在下面的图片中,您可以看到问题会话的根路径,您可以看到只存在弱引用链。

      enter image description here

      请帮忙。 或者至少提出一些建议。

1 个答案:

答案 0 :(得分:0)

正如霍尔格的评论中指出的那样,以下是错误的。我暂时将其留在这里,以便其他人不会走错路。

无论如何,我建议摆脱可变hashCode和整个MEMORY_SESSION_MANAGER(至少在使用会话之前更改userId作为关键)。

错误...

可变hashCode ......这很糟糕,但只要它永远不会改变after它就会被使用。但我可以看到

    Session session = new Session();
    MEMORY_SESSION_MANAGER.addSession(session);
    session.setUserId(userId);

您将其放在HashMap a键中并立即更改hashCode。现在,行为未定义,地图可以在任何访问中抛出或执行无限循环。

如果是C / C ++,它也可能会吃掉你的猫。

真正发生的是钥匙不再被发现。所以它不能被移除,而不是.remove(key)可能也不能被清除。它无法被发现,因此无法将其删除。 That's it.。如果错误hashCode,则会搜索错误的广告位。

  

结果收集了Session对象的所有强引用后,必须从WeakHashMap中删除它。

只有在您遵守合同的情况下,必须遵守合同。你没有。