DragonWind

关于引用池

实现原理其实很简单:根据传入的类型,获取对应的容器,在容器里进行引用的生成和归还操作。

但是要注意容量不能无限扩张

比如在游戏里,可能20%的时间在打副本,这时候某个类型引用池一直在涨,大概峰值是5000个。但是80%的时间在空闲状态,这时候某个类型引用池峰值可能就50个。导致内存得不到效率利用,80%的时间里浪费了4500个。

可以考虑定时检查容量大小的操作,比如每隔5分钟检查引用池大小,超出阈值后就做清除/缩容处理。

这里提供一种基于历史行为的容量预测器,自适应根据引用池历史记录来进行动态扩缩的方法。大概思想是,检测一段的波峰和波谷,将容量设为合适的大小。

如果波动超过了容量,那么扩容。

如果在一段使用频率内,波动比容量的一半还要小,那么缩容。

private void CheckCapacity()
{
    uint currentCount = (uint)m_References.Count;
    if (currentCount < m_CurrentIntervalMinCount)
    {
        m_CurrentIntervalMinCount = currentCount;
        uint currentIntervalNetOutflow = m_CurrentIntervalMaxCount - m_CurrentIntervalMinCount;
        if (currentIntervalNetOutflow > m_RecentMaxIntervalNetOutflow)
        {
            m_RecentMaxIntervalNetOutflow = currentIntervalNetOutflow;
            if (m_RecentMaxIntervalNetOutflow >= m_Capacity)
            {
                uint biggerCapacity = (uint)(m_Capacity * s_CapacityIncreaseRatio);
                ResetCapacity(biggerCapacity);
            }
        }
    }
    else if (currentCount > m_CurrentIntervalMaxCount)
    {
        m_CurrentIntervalMinCount = currentCount;
        m_CurrentIntervalMaxCount = currentCount;
    }

    if (++m_RecentSampleCount >= m_RecentSampleMaxCount)
    {
        uint smallerCapacity = (uint)(m_Capacity / s_CapacityIncreaseRatio);
        if (m_RecentMaxIntervalNetOutflow < smallerCapacity)
        {
            ResetCapacity(smallerCapacity);
        }
        else
        {
            ResetCapacity(m_Capacity);
        }
    }
}

private void ResetCapacity(uint capacity)
{
    if (capacity < s_OriginalCapacity)
    {
        capacity = s_OriginalCapacity;
    }

    if (m_References.Count > capacity)
    {
        Remove((uint)(m_References.Count - capacity));
    }

    m_Capacity = capacity;
    m_CurrentIntervalMinCount = 0;
    m_CurrentIntervalMaxCount = 0;
    m_RecentMaxIntervalNetOutflow = 0;
    m_RecentSampleCount = 0;
    m_RecentSampleMaxCount = (uint)(m_Capacity * s_CapacityCheckPeriodRatio);
}