atomic.Value 中存储的数据是否会被 GC
package mainimport ("fmt""log""math/rand""reflect""sort""sync""sync/atomic""time"
)// 模拟 DAO 层的数据结构
type Config struct {ID int64Name stringActive boolSort int
}// 声明接口供 mock 和注入使用
type IConfigDao interface {GetAll() ([]*Config, error)
}// 内存模拟实现
type ConfigDao struct{}func (d *ConfigDao) GetAll() ([]*Config, error) {// 模拟从数据库获取数据return []*Config{{ID: 1, Name: "Config-A", Active: true, Sort: 10},{ID: 2, Name: "Config-B", Active: false, Sort: 5},{ID: 3, Name: "Config-C", Active: true, Sort: 15},}, nil
}// 缓存池结构体
type ConfigPool struct {wg sync.WaitGroupstopC chan boolconfigDao IConfigDao// 缓存字段configsMap atomic.Value // map[int64]*Config
}// 启动定时刷新
const ReloadInterval = 5 * time.Secondfunc (p *ConfigPool) Start() {p.stopC = make(chan bool)_ = p.loadConfigs()p.autoReload()
}func (p *ConfigPool) Close() {if p.stopC != nil {close(p.stopC)}p.wg.Wait()
}func (p *ConfigPool) loadConfigs() error {rows, err := p.configDao.GetAll()if err != nil || len(rows) == 0 {return err}m := make(map[int64]*Config, len(rows))for _, row := range rows {m[row.ID] = row}p.configsMap.Store(m)log.Println("【loadConfigs】Loaded configs:", len(rows))return nil
}func (p *ConfigPool) autoReload() {p.wg.Add(1)go func() {defer p.wg.Done()random := rand.Intn(10)ticker := time.Tick(ReloadInterval + time.Duration(random)*time.Second)for {select {case <-ticker:_ = p.loadConfigs()case <-p.stopC:log.Println("【autoReload】Stopped")return}}}()
}// 获取单个配置
func (p *ConfigPool) GetConfigByID(id int64) *Config {load := p.configsMap.Load()if load == nil {return nil}configs := load.(map[int64]*Config)return configs[id]
}// 分页查询
func (p *ConfigPool) GetConfigsByPage(page, size int) ([]*Config, int) {load := p.configsMap.Load()if load == nil {return nil, 0}configsMap := load.(map[int64]*Config)// 转为 slice 排序后分页keys := make([]int64, 0, len(configsMap))for k := range configsMap {keys = append(keys, k)}sort.Slice(keys, func(i, j int) bool {return keys[i] < keys[j]})var sorted []*Configfor _, k := range keys {sorted = append(sorted, configsMap[k])}// 按 Sort 字段排序sort.Slice(sorted, func(i, j int) bool {return sorted[i].Sort < sorted[j].Sort})start := (page - 1) * sizeend := start + sizeif start >= len(sorted) {return nil, len(sorted)}if end > len(sorted) {end = len(sorted)}return sorted[start:end], len(sorted)
}// ================== Main 函数测试 ==================func main() {// 初始化 poolpool := &ConfigPool{configDao: &ConfigDao{},stopC: make(chan bool),}pool.Start()defer pool.Close()// 等待加载完成time.Sleep(1 * time.Second)// 测试 GetConfigByIDfmt.Printf("GetConfigByID(1): %+v\n", pool.GetConfigByID(1))// 测试分页查询if list, total := pool.GetConfigsByPage(1, 2); list != nil {fmt.Printf("GetConfigsByPage(1, 2): total=%d\n", total)for _, c := range list {fmt.Printf(" - ID:%d Name:%s Sort:%d\n", c.ID, c.Name, c.Sort)}}// 持续运行观察 reload 日志time.Sleep(30 * time.Second)
}
demo中的atomic.Value 中存储的数据是否会被 GC
如果 atomic.Value 本身没有被释放(即它所在的结构体或变量仍然可达),那么它内部通过 Store() 存储的数据就不会被 GC 回收。
总结一句话:
只要 atomic.Value 所在的对象还“活着”,它内部存储的数据就不会被 GC。
在你的 demo 中,ConfigPool 是长期运行的缓存池,因此 configsMap 中存储的数据在整个程序生命周期内都不会被回收,除非你主动设置为 nil 或者让整个 ConfigPool 实例失效。
如果你希望减少内存占用,可以考虑定期清理不再使用的 key 或使用弱引用结构(如 sync.Map + atomic.Value 组合优化)。