keycloak~深入了解DefaultSegmentedDataContainer对象解决内存泄漏

  • 源码来自infinispan: git@github.com:infinispan/infinispan.git

基于Infinispan 内部架构的了解,让我为你详细解释 DefaultSegmentedDataContainer 中 TTL 的存储位置:

DefaultSegmentedDataContainer 中 TTL 的存储结构

核心结构层次

DefaultSegmentedDataContainer
│
├── DataSegment[] segments  ← 分段数组(根据 key 的 hash 分配)
│   │
│   └── ConcurrentHashMap<Object, InternalCacheEntry>
│       │
│       └── InternalCacheEntry<K, V>  ← 每个缓存条目对象
│           │
│           ├── key          : K         ← 缓存键
│           ├── value        : V         ← 缓存值
│           ├── metadata     : Metadata  ← 元数据(包含TTL信息)★
│           ├── created      : long      ← 创建时间戳
│           └── lastUsed     : long      ← 最后访问时间戳

TTL 信息的具体位置

TTL 不是存储在 DefaultSegmentedDataContainer 的直接属性中,而是在每个 InternalCacheEntry 对象的 Metadata 属性里。

Infinispan 中有多种 InternalCacheEntry 实现类,根据是否有过期时间分类:

实现类 特点 TTL 相关属性
ImmortalCacheEntry 永不过期 无 TTL 属性
MortalCacheEntry 有 lifespan lifespan, created
TransientCacheEntry 有 maxIdle maxIdle, lastUsed
TransientMortalCacheEntry 同时有两者 lifespan, maxIdle, created, lastUsed
MetadataImmortalCacheEntry 使用 Metadata 对象 metadata.lifespan(), metadata.maxIdle()
MetadataMortalCacheEntry 使用 Metadata 对象 metadata.lifespan(), metadata.maxIdle()

下面的缓存sessions表示永不过期,因为它的类型是ImmortalCacheEntry

图片

下面的缓存sessions是有过期时间的,它的类型是TransientMortalCacheEntry

图片

实际的 TTL 属性

对于 Keycloak 的会话缓存,通常使用 **带 Metadata 的条目**,TTL 信息在:
InternalCacheEntry entry = ...;

// 获取 lifespan (生命周期 TTL)
long lifespan = entry.getLifespan();      // 返回毫秒值,-1 表示永不过期

// 获取 maxIdle (空闲 TTL)  
long maxIdle = entry.getMaxIdle();        // 返回毫秒值,-1 表示永不过期

// 获取创建时间
long created = entry.getCreated();        // 创建时的时间戳

// 获取最后访问时间
long lastUsed = entry.getLastUsed();      // 最后访问的时间戳

// 检查是否过期
boolean expired = entry.isExpired(System.currentTimeMillis());

// 计算过期时间点
long expiryTime = entry.expiryTime();     // 返回具体的过期时间戳

在 JVM 调试时查看 TTL

如果你想在调试器中查看某个缓存项的 TTL,路径是:

DefaultSegmentedDataContainer
  └── segments (DataSegment[])
      └── [segment index] 
          └── entries (ConcurrentHashMap)
              └── [key]
                  └── InternalCacheEntry 对象
                      ├── metadata
                      │   ├── lifespan   ← 这里是 TTL(毫秒)
                      │   └── maxIdle    ← 这里是空闲超时(毫秒)
                      ├── created        ← 创建时间戳
                      └── lastUsed       ← 最后访问时间戳

过期判断逻辑

// 在 InternalCacheEntry 中的过期检查逻辑(简化版):
public boolean isExpired(long now) {
    // lifespan 检查
    if (lifespan > 0 && (created + lifespan) <= now) {
        return true;
    }
    // maxIdle 检查
    if (maxIdle > 0 && (lastUsed + maxIdle) <= now) {
        return true;
    }
    return false;
}

Keycloak 中 TTL 的设置来源

当 Keycloak 写入会话时,TTL 值来自 SessionTimeouts 类的计算:

// 写入缓存时设置 TTL
cache.put(key, value, lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS);

这些值最终会被封装到 InternalCacheEntrymetadata 或对应属性中。

DefaultSegmentedDataContainer
│
├── DataSegment[] segments  ← 分段数组(根据 key 的 hash 分配)
│   │
│   └── ConcurrentHashMap<Object, InternalCacheEntry>
│       │
│       └── InternalCacheEntry<K, V>  ← 每个缓存条目对象
│           │
│           ├── key          : K         ← 缓存键
│           ├── value        : V         ← 缓存值
│           ├── metadata     : Metadata  ← 元数据(包含TTL信息)★
│           ├── created      : long      ← 创建时间戳
│           └── lastUsed     : long      ← 最后访问时间戳
InternalCacheEntry entry = ...;

// 获取 lifespan (生命周期 TTL)
long lifespan = entry.getLifespan();      // 返回毫秒值,-1 表示永不过期

// 获取 maxIdle (空闲 TTL)  
long maxIdle = entry.getMaxIdle();        // 返回毫秒值,-1 表示永不过期

// 获取创建时间
long created = entry.getCreated();        // 创建时的时间戳

// 获取最后访问时间
long lastUsed = entry.getLastUsed();      // 最后访问的时间戳

// 检查是否过期
boolean expired = entry.isExpired(System.currentTimeMillis());

// 计算过期时间点
long expiryTime = entry.expiryTime();     // 返回具体的过期时间戳
DefaultSegmentedDataContainer
  └── segments (DataSegment[])
      └── [segment index] 
          └── entries (ConcurrentHashMap)
              └── [key]
                  └── InternalCacheEntry 对象
                      ├── metadata
                      │   ├── lifespan   ← 这里是 TTL(毫秒)
                      │   └── maxIdle    ← 这里是空闲超时(毫秒)
                      ├── created        ← 创建时间戳
                      └── lastUsed       ← 最后访问时间戳
// 在 InternalCacheEntry 中的过期检查逻辑(简化版):
public boolean isExpired(long now) {
    // lifespan 检查
    if (lifespan > 0 && (created + lifespan) <= now) {
        return true;
    }
    // maxIdle 检查
    if (maxIdle > 0 && (lastUsed + maxIdle) <= now) {
        return true;
    }
    return false;
}
// 写入缓存时设置 TTL
cache.put(key, value, lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS);
posted @ 2026-01-30 14:53  张占岭  阅读(5)  评论(0)    收藏  举报