--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