--lua内存管理器 定时清理没有被引用的对象
|
|
LuaMemManager = LuaMemManager or BaseClass(nil, true)
|
|
local rawset = rawset
|
|
local rawget = rawget
|
|
local LuaMemManager = LuaMemManager
|
|
local Time = Time
|
|
local string = string
|
|
local string_gsub = string.gsub
|
|
local string_sub = string.sub
|
|
local string_find = string.find
|
|
local string_len = string.len
|
|
local table_insert = table.insert
|
|
local package_loaded = package.loaded
|
|
local table_insert = table.insert
|
|
local table_remove = table.remove
|
|
|
|
|
|
--直接加载的匹配规则
|
|
LuaMemManager.DIRECT_REQUIRE_CLASS_LIST = {
|
|
-- "^game%.common%."
|
|
-- ,"^game%.scene%."
|
|
{"EventName",-9}
|
|
,{"UiFactory",-9}
|
|
,{"UIHelper",-8}
|
|
,{"UIObjPool",-9}
|
|
,{"GameDefine",-10}
|
|
,{"GlobalEvents",-12}
|
|
,{"scripts",-7}
|
|
,{"UIProperty",-10}
|
|
,{"Scene",-5}
|
|
,{"SceneConfig",-11}
|
|
,{"PoseState",-9}
|
|
,{"Controller",-10}
|
|
,{"Ctrl",-4}
|
|
,{"ChatController_TWO",-18}
|
|
,{"TweenLite",-9}
|
|
,{"CookieWrapper",-13}
|
|
,{"CheatCommand",-12}
|
|
,{"Util", -4}
|
|
-- ,"Helper"}
|
|
,{"module",-6}
|
|
,{"Module",-6}
|
|
,{"Require",-7}
|
|
,{"Require",0}
|
|
,{"Manager",-7}
|
|
,{"Mgr",-3}
|
|
-- ,"Effect"}
|
|
,{"MainRoleVo",-10}
|
|
,{"Requires",-8}
|
|
,{"Model",-5}
|
|
,{"Action",1}
|
|
,{"Jury",1}
|
|
,{"Const",-5}--不加的话会经常被释放啊
|
|
,{"Functor", 1}
|
|
,{"UIComponents", 1}
|
|
,{"ItemListCreator", 1}
|
|
,{"Test", 1}
|
|
}
|
|
|
|
--重新登录不清除缓存的model
|
|
LuaMemManager.KEEP_CACHE_MODEL = {
|
|
["LoginModel"] = true,
|
|
["ServerModel"] = true,
|
|
["UIModelClassByRT"] = true,
|
|
["BaseModel"] = true,
|
|
["GameSettingModel"] = true,
|
|
}
|
|
|
|
function LuaMemManager:ForceReload()
|
|
package_loaded["utils.ReloadModules"] = nil
|
|
originalRequire("utils.ReloadModules")
|
|
for i,v in ipairs(ReloadModules.Names) do
|
|
package_loaded[v] = nil
|
|
if not self:checkClassStatus(v) then
|
|
originalRequire(v)
|
|
logWarn("ForceReload:Reload Module=>Reload "..v.." successfuly!!")
|
|
else
|
|
local class_name = LuaMemManager.GetLastStr(v,".")
|
|
local ct = self.class_list[class_name]
|
|
if not ct then
|
|
logWarn("ForceReload:Reload Module:Module=> "..v.." has not been required yet!!")
|
|
originalRequire(v)
|
|
else
|
|
if not ct.isLoad then
|
|
ct.isLoad = true
|
|
ct.last_used_Time = Time.time
|
|
end
|
|
logWarn("ForceReload:Reload Module=>Reload "..v.." successfuly!!")
|
|
originalRequire(v)
|
|
end
|
|
end
|
|
end
|
|
ReloadModules.ReloadCommand()
|
|
end
|
|
|
|
function LuaMemManager:__init()
|
|
LuaMemManager.Instance = self
|
|
self.clear_mem_start_time = 0
|
|
|
|
self:initConfigInfo()
|
|
self:initGameClassInfo()
|
|
collectgarbage("setpause", 110)
|
|
collectgarbage("setstepmul", 200)
|
|
--重写require
|
|
local function moduleRequire(config_path,config_name, auto_delete_time, cannot_force_delete)
|
|
|
|
--保存配置文件信息
|
|
if config_name then
|
|
if not self.config_list[config_name] then
|
|
self.config_list[config_name] = {isLoad = false, config_path = config_path, last_used_Time = 0}
|
|
if auto_delete_time then
|
|
self.config_list[config_name].auto_delete_time = auto_delete_time
|
|
end
|
|
if cannot_force_delete then
|
|
self.config_list[config_name].cannot_force_delete = cannot_force_delete
|
|
end
|
|
end
|
|
if auto_delete_time and cannot_force_delete then
|
|
if Config[config_name] then end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
|
|
--保存代理类信息
|
|
local class_name = LuaMemManager.GetLastStr(config_path,".")
|
|
--保存普通类信息
|
|
if class_name and (string_find(config_path,"game.",1,true) == 1
|
|
or string_find(config_path,"sysinfo.",1,true) == 1
|
|
or string_find(config_path,"utils.",1,true) == 1
|
|
or string_find(config_path,"gameinput.",1,true) == 1
|
|
or string_find(config_path,"Language.",1,true) == 1
|
|
or string_find(config_path,"operation.",1,true) == 1
|
|
or string_find(config_path,"newgameui.",1,true) == 1
|
|
or string_find(config_path,"UIComponent.",1,true) == 1) and LuaMemManager.checkClassStatus(self, config_path) then
|
|
if self.class_list[class_name] == nil then
|
|
self.class_list[class_name] = {isLoad = false, config_path = config_path, last_used_Time = 0}
|
|
end
|
|
return nil
|
|
end
|
|
|
|
if string_find(config_path,"Model",-5,true) then
|
|
self.model_list[class_name] = {isLoad = false, config_path = config_path}
|
|
end
|
|
|
|
originalRequire(config_path)
|
|
end
|
|
_G.originalRequire = _G.require
|
|
_G.require = moduleRequire
|
|
|
|
end
|
|
|
|
function LuaMemManager:InitInfo()
|
|
local function onUpdateFunc()
|
|
self:checkToReleaseMem()
|
|
end
|
|
TimerQuest.AddPeriodQuest(GlobalTimerQuest, onUpdateFunc, 55, -1)
|
|
end
|
|
|
|
function LuaMemManager:ClearMemory()
|
|
-- print("强制垃圾回收")
|
|
-- collectgarbage("collect")
|
|
collectgarbage()
|
|
end
|
|
|
|
|
|
function LuaMemManager:getInstance()
|
|
if LuaMemManager.Instance == nil then
|
|
LuaMemManager.New()
|
|
end
|
|
return LuaMemManager.Instance
|
|
end
|
|
--将 szFullString 对象拆分为一个子字符串表
|
|
function LuaMemManager.Split(szFullString, szSeparator)
|
|
local nFindStartIndex = 1
|
|
local nSplitIndex = 1
|
|
local nSplitArray = {}
|
|
while true do
|
|
local nFindLastIndex = string_find(szFullString, szSeparator, nFindStartIndex,true)
|
|
if not nFindLastIndex then
|
|
nSplitArray[nSplitIndex] = string_sub(szFullString, nFindStartIndex, string_len(szFullString))
|
|
break
|
|
end
|
|
table_insert(nSplitArray, string_sub(szFullString, nFindStartIndex, nFindLastIndex - 1))
|
|
nFindStartIndex = nFindLastIndex + string_len(szSeparator)
|
|
nSplitIndex = nSplitIndex + 1
|
|
end
|
|
return nSplitArray
|
|
end
|
|
|
|
--获取最后一个字符串
|
|
function LuaMemManager.GetLastStr(szFullString, szSeparator)
|
|
local nFindStartIndex = 1
|
|
local nSplitIndex = 1
|
|
while true do
|
|
local nFindLastIndex = string_find(szFullString, szSeparator, nFindStartIndex,true)
|
|
if not nFindLastIndex then
|
|
return string_sub(szFullString, nFindStartIndex, string_len(szFullString))
|
|
end
|
|
nFindStartIndex = nFindLastIndex + string_len(szSeparator)
|
|
nSplitIndex = nSplitIndex + 1
|
|
end
|
|
end
|
|
|
|
--初始化配置信息
|
|
function LuaMemManager:initConfigInfo()
|
|
self.check_config_time = 0
|
|
--Config为所有配置属性的父节点
|
|
Config = Config or {}
|
|
self.config_list = {}
|
|
self.model_list = {}
|
|
|
|
setmetatable(Config,{__newindex = function (t,k,v)
|
|
rawset(Config,k,v)
|
|
if v == nil then
|
|
LuaMemManager.resetPackageLoaded(self, k)
|
|
end
|
|
end
|
|
,
|
|
__index = function (t,k)
|
|
local ct = self.config_list[k]
|
|
if ct and not ct.isLoad then
|
|
-- print("------------------------创建配置 ",k)
|
|
ct.isLoad = true
|
|
ct.last_used_Time = Time.time
|
|
originalRequire(ct.config_path)
|
|
end
|
|
return rawget(Config,k)
|
|
end}
|
|
)
|
|
end
|
|
|
|
|
|
--初始化类的信息
|
|
function LuaMemManager:initGameClassInfo()
|
|
self.check_class_time = 0
|
|
self.class_list = {}
|
|
--_G为所有全局变量的父节点
|
|
setmetatable(_G,{__newindex = function (t,k,v)
|
|
-- if k == "data" then
|
|
-- local info = debug.getinfo(2, "Sl")
|
|
-- print("全局变量:" .. k .. ", 来源:"..info.source)
|
|
-- end
|
|
if v == nil then
|
|
local ct = self.class_list[k]
|
|
if ct then
|
|
ct.isLoad = false
|
|
if package_loaded[ct.config_path] then
|
|
package_loaded[ct.config_path] = nil
|
|
end
|
|
end
|
|
end
|
|
rawset(_G ,k ,v)
|
|
end
|
|
,
|
|
__index = function (t,k)
|
|
local ct = self.class_list[k]
|
|
if ct and not ct.isLoad then
|
|
-- print("创建类 ",k)
|
|
ct.isLoad = true
|
|
ct.last_used_Time = Time.time
|
|
originalRequire(ct.config_path)
|
|
end
|
|
return rawget(_G ,k)
|
|
end}
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
--检测是否是需要处理的类文件
|
|
function LuaMemManager:checkClassStatus(config_path)
|
|
for i,vo in pairs(LuaMemManager.DIRECT_REQUIRE_CLASS_LIST) do
|
|
if string_find(config_path,vo[1],vo[2],true) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
--把配置对象置空
|
|
function LuaMemManager:clearConfigPro(class_name)
|
|
Config[class_name] = nil
|
|
LuaMemManager.resetPackageLoaded(self, class_name)
|
|
end
|
|
|
|
|
|
--去掉配置对象在package的引用
|
|
function LuaMemManager:resetPackageLoaded(class_name)
|
|
local ct = self.config_list[class_name]
|
|
if ct then
|
|
-- print("销毁配置 ",class_name)
|
|
ct.isLoad = false
|
|
if package_loaded[ct.config_path] then
|
|
package_loaded[ct.config_path] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
--清理类对象
|
|
function LuaMemManager:clearGameClass(class_type)
|
|
if class_type then
|
|
local now_super = class_type
|
|
local delete_list = nil
|
|
local child_will_delete = true
|
|
local t = nil
|
|
local class_name = nil
|
|
while now_super ~= nil do
|
|
-- print("now_super._source=",now_super._source)
|
|
if now_super._source then
|
|
class_name = LuaMemManager.GetLastStr(now_super._source,"/")
|
|
class_name = string_gsub(class_name,".lua","")
|
|
if class_name and self.class_list[class_name] then
|
|
if child_will_delete then --只有子类要被删除 才去判断父类是否需要去掉一个作为父类的引用
|
|
if _be_super_count_map[now_super] then
|
|
_be_super_count_map[now_super] = _be_super_count_map[now_super] - 1
|
|
end
|
|
--如果该类引用的实力对象的引用次数为0 而且 该类没有作为任何类的父类 才需要被删除
|
|
if (_in_obj_ins_map[now_super] == nil or _G.next(_in_obj_ins_map[now_super]) == nil) and (_be_super_count_map[now_super] == nil or _be_super_count_map[now_super] == 0) then
|
|
delete_list = delete_list or {}
|
|
table_insert(delete_list,class_name)
|
|
child_will_delete = true
|
|
else
|
|
child_will_delete = false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
now_super = now_super.super
|
|
end
|
|
if delete_list then
|
|
for i = 1,#delete_list do
|
|
-- print("销毁类 ",delete_list[i])
|
|
LuaMemManager.resetGameClassPackageLoaded(self, delete_list[i])
|
|
_G[delete_list[i]] = nil
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--清理类对象在package的引用
|
|
function LuaMemManager:resetGameClassPackageLoaded(className)
|
|
local ct = self.class_list[className]
|
|
if ct then
|
|
ct.isLoad = false
|
|
if package_loaded[ct.config_path] then
|
|
package_loaded[ct.config_path] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
function LuaMemManager:checkToReleaseMem(forceToRelease, hot_update)
|
|
--检测配置是否需要释放内存
|
|
forceToRelease = forceToRelease or hot_update
|
|
if forceToRelease or (self.check_config_time == 0 or Time.time - self.check_config_time > 100) then --检测是否有需要清理的配置对象
|
|
self.check_config_time = Time.time
|
|
local can_force_release = forceToRelease
|
|
for class_name,vo in pairs(self.config_list) do
|
|
if vo.isLoad then
|
|
if not hot_update and vo.cannot_force_delete then
|
|
can_force_release = false
|
|
end
|
|
if can_force_release then
|
|
if Time.time - vo.last_used_Time < 0.5 then --0.5秒的最大限度加载时间
|
|
can_force_release = false
|
|
end
|
|
end
|
|
if can_force_release or (Time.time - vo.last_used_Time > (vo.auto_delete_time or 100)) then --5秒没被引用就销毁
|
|
LuaMemManager.clearConfigPro(self, class_name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--检测类是否需要释放内存
|
|
if forceToRelease or ((self.check_class_time == 0 or Time.time - self.check_class_time >= 50)) then --检测是否有需要清理的类对象 调试阶段先用1
|
|
self.check_class_time = Time.time
|
|
local class_type = nil
|
|
for class_name,vo in pairs(self.class_list) do
|
|
if vo.isLoad then--当前已经被加载
|
|
if Time.time - vo.last_used_Time > 0.5 then --0.5秒的最大限度加载时间
|
|
class_type = _G[class_name]
|
|
if class_type
|
|
and (_be_super_count_map[class_type] == nil or _be_super_count_map[class_type] == 0) --没有作为父类的引用
|
|
and (_in_obj_ins_map[class_type] == nil or _G.next(_in_obj_ins_map[class_type]) == nil)--没有实例化的对象
|
|
then
|
|
LuaMemManager.clearGameClass(self, class_type)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if forceToRelease then -- or self.clear_mem_start_time == 0 or Time.time - self.clear_mem_start_time >= 3 then
|
|
-- self.clear_mem_start_time = Time.time
|
|
--if forceToRelease or collectgarbage("count") > 20000 then
|
|
LuaMemManager.ClearMemory()
|
|
-- end
|
|
end
|
|
end
|
|
|
|
--清除所有model的缓存
|
|
function LuaMemManager:ClearModelData( )
|
|
for class_name,v in pairs(self.model_list) do
|
|
if not LuaMemManager.KEEP_CACHE_MODEL[class_name] and _G[class_name]
|
|
and _G[class_name].Instance then
|
|
--执行init函数
|
|
local instance = _G[class_name].Instance
|
|
instance._class_type.__init(instance)
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
--清除所有model的缓存
|
|
function LuaMemManager:ClearModelData( )
|
|
if self.is_clearing_model then
|
|
return
|
|
end
|
|
self.delay_clear_model_list = {}
|
|
for class_name,v in pairs(self.model_list) do
|
|
table_insert(self.delay_clear_model_list, class_name)
|
|
end
|
|
self.is_clearing_model = true
|
|
local function on_update( )
|
|
self:ClearModelOnUpdate()
|
|
end
|
|
if not self.period_clear_id then
|
|
self.period_clear_id = GlobalTimerQuest:AddPeriodQuest(on_update, 0.02, -1)
|
|
end
|
|
end
|
|
|
|
function LuaMemManager:ClearModelOnUpdate( )
|
|
local size = #self.delay_clear_model_list
|
|
if size > 0 then
|
|
local len = 3
|
|
for i = 1, len do
|
|
local class_name = table_remove(self.delay_clear_model_list, 1)
|
|
if class_name and not LuaMemManager.KEEP_CACHE_MODEL[class_name] and _G[class_name]
|
|
and _G[class_name].Instance then
|
|
--执行init函数
|
|
local instance = _G[class_name].Instance
|
|
instance._class_type.__init(instance)
|
|
end
|
|
end
|
|
else
|
|
if self.period_clear_id then
|
|
GlobalTimerQuest:CancelQuest(self.period_clear_id)
|
|
self.period_clear_id = nil
|
|
end
|
|
self.is_clearing_model = false
|
|
end
|
|
end
|
|
]]
|
|
function LuaMemManager:__delete()
|
|
|
|
end
|