gpt4 book ai didi

java - 弱引用。当只剩下 WeakReferences 时,对象不会被删除

转载 作者:行者123 更新时间:2023-12-05 07:47:25 27 4
gpt4 key购买 nike

在我们的系统中,我们将客户端 session 呈现为类 session 。

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

系统中存在两个 session 管理器。

  1. 客户端 session 管理器 - 保持活跃的客户端 session 是客户端。该管理器内部是简单的 ConcurrentHashMap< Long, Session >,其中键是用户 ID。
  2. 内存 session 管理器 - 保存 GC 尚未收集的 session 。里面有WeakHashMap < Session, Long > (这里的value是user id)

当客户端连接并登录时,下一个流程发生:

  1. 已创建客户端 session 。
  2. Session 放入 Memory Sessions Manager(此时 hashcode 为 0,因此它们被放入单个桶中)
  3. 客户端已登录, session 已使用正确的哈希码放入客户端 session 管理器。
  4. 客户端 session 已关闭并从 Client Sessions Manager 中删除。

作为收集 Session 对象上所有强引用后的结果,它必须从 WeakHashMap 中删除。(WeakHashMap 中的条目将在以下情况下自动删除 它的 key 不再正常使用。更准确地说,存在一个 给定键的映射不会阻止键被丢弃 垃圾收集器,也就是说,使其可终结、终结,然后回收。)但出于某种原因,他们留在那里。代码如下:

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

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

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 循环后,in 仍保留在内存中。这是 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

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

enter image description here

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

最佳答案

正如 Holger 在评论中指出的那样,以下内容是错误的。我暂时将其保留在这里,以免其他人走上这条错误的道路。

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

错...

Mutable hashCode... 这很糟糕,但可以接受,只要它在使用后 永远不会改变。然而我能看见

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

你把它放在一个 HashMap 中,并立即更改 hashCode。现在,行为未定义, map 可以在任何访问时抛出或执行无限循环。

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

真正发生的是 key 再也找不到了。所以它不能被删除,不是 .remove(key) probably 也不在清理中。找不到,所以无法删除。 That's it. .使用错误的 hashCode 会搜索错误的槽。

As result after collection of all strong references on Session object it must be removed from WeakHashMap.

必须只有在你遵守契约(Contract)的情况下才遵守契约(Contract)。而你没有。

关于java - 弱引用。当只剩下 WeakReferences 时,对象不会被删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39764809/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com