erlang进程占用内存过多的查找问题过程

erlang进程占用内存过多的查找问题过程

最近开发一个机器人程序,其主要功能模拟玩家在地图上跑动用来测试地图压力。主要功能代码如下:

loop(State) ->#state{robot_state = RobotState} = State,case RobotState ofborn ->NewState = robot_born(State),NextRobotState = get_next_robot_state(),erlang:send_after(random:uniform(3000), self(), loop),NewState#state{robot_state = NextRobotState};move ->robot_move(State),State;move_to_npc ->robot_move_to_npc(State),State;_ ->todoend.
robot_move_to_npc(State) ->#state{robot_player_id = RobotPlayerID, send_socket = _SendSocket} = State,{CurMapDataID, CX, CY} = get_cur_robot_pos(State),{TX, TY} = get_a_npc_point(CurMapDataID, CX, CY),PathList = aStar:find_path(CurMapDataID, CX div 100, CY div 100, TX, TY),case PathList of[] ->erlang:send_after(5000, self(), loop);PathList ->PosInfoList = [#pk_PosInfo{x = X * 100, y = Y * 100} || {X, Y} <- PathList],debug:rpc_call(RobotPlayerID, pp_move, on_C2GS_PlayerMoveTo, [undefined, #pk_C2GS_PlayerMoveTo{posX = CX, posY = CY, curMapDataId = CurMapDataID,   posInfos = PosInfoList}]),erlang:put(move_target, {TX, TY}),erlang:send_after(1000, self(), {check_move_complete, 0})end.
<span ></span><pre name="code" >get_a_npc_point(MapDataID, CX, CY) -><span ></span>NPCList = data_npc:get_items(),<span ></span>F = fun(#npcCfg{map = Map}) -><span ></span>Map =:= MapDataID<span ></span>end,<span ></span>MapNPCList = lists:filter(F, NPCList),<span ></span>#npcCfg{posx = TX, posy = TY} = lists:nth(random:uniform(length(MapNPCList)), MapNPCList),<span ></span>{TargetX, TargetY} = mod_mirror:get_fix_point(CX div 100, CY div 100, TX, TY, 2, MapDataID),<span ></span>{TargetX div 100, TargetY div 100}.
 
<span >如上所示,一个机器人就是一个单独的进程,机器人进程start起来后每隔一段loop,loop主要做的事情就是寻找一个点,并且让机器人移动过去。很简单的一个功能,但是出现了问题。在调试过程中发现一个机器人进程占用内存资源太多,居然有40M之多。在虚拟机上测试,1G内存在几秒时间内消耗90%以上。在手动gc的情况下,内存占用回到16M,但是还是太多。</span>
 

怀疑自己写的代码有问题,祭出神器——逐层扫描调试。首先把loop的主要功能屏蔽,机器人进程不干任何事情,process_info(PID, memory)查看内存占用,意料之中得到{memory,2560}。

第二步,屏蔽掉A*寻路过程,留下寻找目标点的过程。部分函数修改如下

robot_move_to_npc(State) ->#state{robot_player_id = RobotPlayerID, send_socket = _SendSocket} = State,{CurMapDataID, CX, CY} = get_cur_robot_pos(State),{TX, TY} = get_a_npc_point(CurMapDataID, CX, CY),todo.
查看内存占用大概20M左右,说明在寻找目标点的函数get_a_npc_point/3有大量内存消耗。

第三步,进入get_a_npc_point/3,继续第二步方法,拆分每个语句,查看占用情况,修改如下

get_a_npc_point(MapDataID, _CX, _CY) ->NPCList = data_npc:get_items(),F = fun(#npcCfg{map = Map}) ->Map =:= MapDataIDend,MapNPCList = lists:filter(F, NPCList),#npcCfg{posx = TX, posy = TY} = lists:nth(random:uniform(length(MapNPCList)), MapNPCList),{TX, TY}.
修改之后,内存占用情况大为好转,显然问题在mod_mirror:get_fix_point这个方法上。这个方法的目的是根据给定的一个点,随机返回其周围八个方向的一个可以行走的点。在判断随机出来的点是否可以行走时,会根据给定的MapID在地图数据ETS表中查找该地图中所有不可走的点来做判断。由于ets:lookup/2查找出来地图数据结构过大,EVM在执行完该ets:lookup/2之后,需要在进程中开辟出更多的空间来存储,所以进程占用的内存就变多了。但是这种机制是erlang本身的内存管理方式,程序猿并没办法改变。

结论:1、erlang每次执行的结果都会在进程里开辟空间来存储,如果执行结果赋值给了一个变量,那么该变量只是执行结果所在内存的一个指针。2、如果一个进程内存能通过手动gc明显降下来,说明很可能和你的代码无关,是执行结果太过大。3、如果在这种情况下需要减少进程占用的内存空间,可以在代码里面添加erlang:garbage_collect,前提是你的进程有足够的处理时间来做gc(在这个内存不值钱的年代不提议这样做,尤其是在对处理速度要求非常高的情况下)。

附:

1、初始占用内存

(gameserver_1@localhost)8> erlang:process_info(erlang:whereis(robot_10_1), memory).
{memory,33657600}

2、删除mod_mirror:get_fix_point这个方法后占用内存

(gameserver_1@localhost)16> erlang:process_info(erlang:whereis(robot_10_1), memory).
{memory,2543448}

3、不删除mod_mirror:get_fix_point这个方法,增加erlang:garbage_collect的内存占用

(gameserver_1@localhost)22> erlang:process_info(erlang:whereis(robot_10_54), memory). {memory,4714992}

免责声明:本网信息来自于互联网,目的在于传递更多信息,并不代表本网赞同其观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,并请自行核实相关内容。本站不承担此类作品侵权行为的直接责任及连带责任。如若本网有任何内容侵犯您的权益,请及时联系我们,本站将会在24小时内处理完毕。
相关文章
返回顶部