Erlang及其对堆内存的消耗
我一直在我的HP Proliant服务器上运行高度并发的应用程序。 该应用程序是我用erlang编码的文件系统索引器。 它会在文件系统上找到每个文件夹的进程并将所有文件路径记录在碎片化的Mnesia数据库中。 (数据库由disc_only_copies
类型的表格组成,其文件系统的屏幕截图可以在这里查看。)
执行文件系统的高强度工作的代码片段如下所示:
%%% -------- COPYRIGHT NOTICE -------------------------------------------------------------------- %% @author Muzaaya Joshua, <joshmuza@gmail.com> [http://joshanderlang.blogspot.com] %% @version 1.0 free software, but modification prohibited %% @copyright Muzaaya Joshua (file_scavenger-1.0) 2011 - 2012 . All rights reserved %% @reference <a href="http://www.erlang.org">OpenSource Erlang WebSite</a> %% %%% ---------------- EDOC INTRODUCTION TO THE MODULE ---------------------------------------------- %% @doc This module provides the low level APIs for reading, writing, %% searching, joining and moving within directories.The module implementation %% took place on @date at @time. %% @end -module(file_scavenger_utilities). %%% ------- EXPORTS ------------------------------------------------------------------------------- -compile(export_all). %%% ------- INCLUDES ----------------------------------------------------------------------------- %%% -------- MACROS ------------------------------------------------------------------------------ -define(IS_FOLDER(X),filelib:is_dir(X)). -define(IS_FILE(X),filelib:is_file(X)). -define(FAILED_TO_LIST_DIR(X),error_logger:error_report(["*** File Scavenger Utilities Error ***** ",{error,"Failed to List Directory"},{directory,X}])). -define(NOT_DIR(X),error_logger:error_report(["*** File Scavenger Utilities Error ***** ",{error,"Not a Directory"},{alleged,X}])). -define(NOT_FILE(X),error_logger:error_report(["*** File Scavenger Utilities Error ***** ",{error,"Not a File"},{alleged,X}])). %%%--------- TYPES ------------------------------------------------------------------------------- %% @type dir() = string(). %% Must be containing forward slashes, not back slashes. Must not end with a slash %% after the exact directory.e.g this is wrong: "C:/Program Files/SomeDirectory/" %% but this is right: "C:/Program Files/SomeDirectory" %% @type file_path() = string(). %% Must be containing forward slashes, not back slashes. %% Should include the file extension as well e.g "C:/Program Files/SomeFile.pdf" %% ----------------------------------------------------------------------------------------------- %% @doc Enters a directory and executes the fun ForEachFileFound/2 for each file it finds %% If it finds a directory, it executes the fun %% ForEachDirFound/2. %% Both funs above take the parent Dir as the first Argument. Then, it will spawn an %% erlang process that will spread the found Directory too in the same way as the parent directory %% was spread. The process of spreading goes on and on until every File (wether its in a nested %% Directory) is registered by its full path. %% @end %% %% @spec spread_directory(dir(),dir(),funtion(),function())-> ok. spread_directory(Dir,Top_Directory,ForEachFileFound,ForEachDirFound) when is_function(ForEachFileFound),is_function(ForEachDirFound) -> case ?IS_FOLDER(Dir) of false -> ?NOT_DIR(Dir); true -> F = fun(X)-> FileOrDir = filename:absname_join(Dir,X), case ?IS_FOLDER(FileOrDir) of true -> (catch ForEachDirFound(Top_Directory,FileOrDir)), spawn(fun() -> ?MODULE:spread_directory(FileOrDir,Top_Directory,ForEachFileFound,ForEachDirFound) end); false -> case ?IS_FILE(FileOrDir) of false -> {error,not_a_file,FileOrDir}; true -> (catch ForEachFileFound(Top_Directory,FileOrDir)) end end end, case file:list_dir(Dir) of {error,_} -> ?FAILED_TO_LIST_DIR(Dir); {ok,List} -> lists:foreach(F,List) end end.
spread_directory/4
函数是通用的,它需要两个funs
。 一个有趣的ForEachFileFound/2
: ForEachFileFound/2
将与顶级大多数目录,找到的文件一起使用,并对其做任何事情和其他乐趣: ForEachDirFound/2
将与顶级大多数目录一起,找到并以任何方式使用它。
我为这个应用程序使用的启动脚本确保erlang能够尽可能多地生成进程。 一旦进程完成索引它退出的文件夹。
#!/usr/bin/env sh echo "Starting File Scavenger System. Layer 1 on the P2P File Sharing System....." erl -name file_scavenger@127.0.0.1 +P 13421779 -pa ./ebin ./lib/*/ebin ./include -mnesia dir '"./database"' -mnesia dump_log_write_threshold 10000 -eval "application:load(file_scavenger)" -eval "application:start(file_scavenger)"
有一个gen_server将密集模块和数据库连接起来,在这个数据库中记录所有路径。 下面显示了启动spread_directory工作的片段:
handle_cast(index_dirs,#scavenger{directory_paths = Dirs} = State)-> {File,Folder} = case {State#scavenger.verbose,State#scavenger.verbose_to} of {true,tty} -> { fun(TopDir,Fl)-> io:format(" File: ~p~n",[Fl]), file_scavenger_database:insert_file(filename:basename(Fl),file,Fl,TopDir,filename:extension(Fl)) end, fun(TopDir,Fd) -> io:format(" Folder: ~p~n",[Fd]), file_scavenger_database:insert_file(Fd,folder,Fd,TopDir,undefined) end }; {true,SomeFile}-> { fun(TopDir,Fl)-> os:cmd("echo File: " ++ Fl ++ " >> " ++ SomeFile), file_scavenger_database:insert_file(filename:basename(Fl),file,Fl,TopDir,filename:extension(Fl)) end, fun(TopDir,Fd)-> os:cmd("echo Folder: " ++ Fd ++ " >> " ++ SomeFile), file_scavenger_database:insert_file(Fd,folder,Fd,TopDir,undefined) end } end, Main = fun(Dir) -> error_logger:info_msg("*** File scavenger Server indexing directory: ~p~n",[Dir]), spawn(fun() -> file_scavenger_utilities:spread_directory(Dir,Dir,File,Folder) end) end, lists:foreach(Main,Dirs), {noreply,State}; handle_cast(stop, State) -> {stop, normal, State}.
更多源详细信息可以在整个应用程序中找到。 可以在这里找到完整的源代码和构建应用程序:File_scavenger-1.0.zip。
现在,我在服务器上启动应用程序(HP Proliant G6,包含Intel处理器(2个处理器,每个4核,每个内核2.4 GHz,8 MB高速缓存),20 GB RAM,1.5 TB磁盘空间。这些高性能的机器在我们的处置中,系统数据库应该在两者之间进行复制,每台服务器运行Solaris 10,64位),其终端如下所示:
bash-3.00# sh file_scavenger.sh Starting File Scavenger System. Layer 1 on the P2P File Sharing System..... Erlang R14B03 (erts-5.8.4) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.8.4 (abort with ^G) (file_scavenger@127.0.0.1)1> =INFO REPORT==== 18-Aug-2011::09:36:04 === Starting File Scavenger Database...... =INFO REPORT==== 18-Aug-2011::09:36:04 === Database Successfully Started.... =INFO REPORT==== 18-Aug-2011::09:36:04 === Starting File Scavenger Database...... =INFO REPORT==== 18-Aug-2011::09:36:04 === Database Successfully Started.... =INFO REPORT==== 18-Aug-2011::09:36:04 === File Scavenger Server starting with default verbose settings.... (file_scavenger@127.0.0.1)1> file_scavenger_server:index_dirs().
服务器开始运行并向终端传送所有找到的文件和文件夹。 服务器配备了太多的RAM(20 GB)和交换空间(交换为16 GB)。 然而,它运行了大约18个小时,最后,erlang虚拟机报告了这一点:
File: "/proc/4324/root/opt/csw/gcc4/share/locale/ja/LC_MESSAGES/gcc.mo" Folder: "/proc/4324/root/opt/csw/gcc4/share/locale/da" Folder: "/proc/4324/root/opt/csw/gcc4/share/locale/es/LC_MESSAGES" File: "/proc/4324/root/proc/4984/root/.thumbnails/normal/dc259e3897e8af4b379c6d956b6c1393.png" File: "/proc/4324/root/proc/4984/root/.thumbnails/fail/gnome-thumbnail-factory/223c19786421b7101d14075bdec46f61.png" File: "/proc/4324/root/opt/csw/gcc4/libexec/gcc/i386-pc-solaris2.10/4.5.1/install-tools/mkheaders" File: "/proc/4324/root/opt/csw/gcc4/libexec/gcc/i386-pc-solaris2.10/4.5.1/cc1plus" File: "/proc/4324/root/opt/csw/gcc4/lib/libsupc++.la" Crash dump was written to: erl_crash.dump eheap_alloc: Cannot allocate 153052320 bytes of memory (of type "heap"). Abort - core dumped bash-3.00#
问题1.使用如此强大的服务器,为什么操作系统无法为应用程序提供这样的内存(这是唯一运行的应用程序)?
问题2.我启动的Erlang模拟器被指示能够产生尽可能多的进程。 值+P 13421779
。 Erlang VM无法访问该内存或未能将其分配给其进程?
问题3.对于Solaris,它看到一个进程: epmd
,可能包含并启动数千个微线程。 我可以通过什么样的配置来使Solaris永远不会停止我的应用程序,无论它是多么“饥饿的内存”? 可用交换空间为16 GB,RAM 20 GB,说实话,一定有什么问题。
问题4.我可以在Erlang模拟器上进行哪些配置,以避免这些堆内存崩溃转储,尤其是当它可能需要的所有内存在服务器上可用时? 如果Erlang仍然无法将这样的内存分配给简单的文件系统索引器(以及它的并发),我将如何在此服务器上运行更多的内存消耗应用程序?
最后,我可以做的所有其他调整,以避免在这样的硬件上堆内存问题,是值得欢迎的。 提前致谢
我没有时间看源代码,但这里有一些评论:
问题1.使用如此强大的服务器,为什么操作系统无法为应用程序提供这样的内存(这是唯一运行的应用程序)?
因为Erlang虚拟机试图消耗超过可用空闲内存。
问题2.我启动的Erlang模拟器被指示能够产生尽可能多的进程。 值+ P 13421779. Erlang VM是否无法访问此内存或未能将其分配给其进程?
不,如果你将用完processessess,Erlang虚拟机会这么说(并且虚拟机仍然可以正常运行):
=ERROR REPORT==== 18-Aug-2011::10:04:04 ===
Error in process <0.31775.138> with exit value: {system_limit,[{erlang,spawn_link, [erlang,apply,[#Fun<shell.3.130303173>,[]]]},{erlang,spawn_link,1},{shell,get_command,5}, {shell,server_loop,7}]}
问题3.对于Solaris,它看到一个进程:epmd,可能包含并启动数千个微线程。 我可以通过什么样的配置来使Solaris永远不会停止我的应用程序,无论它是多么“饥饿的内存”? 可用交换空间为16 GB,RAM 20 GB,说实话,一定有什么问题。
epmd
是Erlang端口映射deamon。 它负责管理分布式Erlang,并且与您的Erlang应用程序无关。 你应该寻找的过程最有可能是名字beam.smp
。 这些将显示Erlang VM的操作系统内存消耗等。
问题4.我可以在Erlang模拟器上进行哪些配置,以避免这些堆内存崩溃转储,尤其是当它可能需要的所有内存在服务器上可用时? 如果Erlang仍然无法将这样的内存分配给简单的文件系统索引器(以及它的并发),我将如何在此服务器上运行更多的内存消耗应用程序?
Erlang虚拟机应该能够使用机器中的所有可用内存。 但是,这取决于您的应用程序如何编写。 内存泄漏可能有很多原因:
上一篇: Erlang and its consumption of Heap Memory
下一篇: How to get all instances from the module loaded in a Ninject kernel?