第一步:
查看进程数目是否正常? erlang:system_info(process_count).
第二步:
查看节点的内存消耗在什么地方?
> erlang:memory().
[{total,2099813400},
{processes,1985444264},
{processes_used,1985276128},
{system,114369136},
{atom,4479545},
{atom_used,4477777},
{binary,22756952},
{code,10486554},
{ets,47948808}]
显示内存大部分消耗在进程上,由此确定是进程占用了大量内存
第三步:
查看哪些进程占用内存最高?
> spawn(fun() -> etop:start([{output, text}, {interval, 1}, {lines, 20}, {sort, memory}]) end).
(以输出text方式启动etop,其间隔为1秒,输出行数为20行,按照内存排序. 这里spawn一个新进程,目的是输出etop数据时不影响erlang shell 输入.)
第四步:
查看占用内存最高的进程状态
> erlang:process_info(pid(0,12571,0)).
[{current_function,{mod_player,send_msg,2}},
{initial_call,{erlang,apply,2}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[<0.12570.0>]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.46.0>},
{total_heap_size,12538050},
{heap_size,12538050},
{stack_size,10122096},
{reductions,3795950},
{garbage_collection,[{min_bin_vheap_size,46368},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
其中” {total_heap_size,12538050},” 表示占用内存为 12358050 words(32位系统word size为4,64位系统word size为8, 可以通过erlang:system_info(wordsize) 查看),在64位系统下将近100M, 太夸张了!
第五步:
手动gc回收,希望问题可以解决
> erlang:garbage_collect(pid(0,12571,0)).
true
再次查看进程内存,发现没有任何变化!gc没有回收到任何资源,因此消耗的内存还在发挥作用,没有回收!
第六步:
不要怀疑系统,首先要怀疑自己的代码
认真观察代码,其大致结构如下:
send_msg(Socket, Pid) ->
try
receive
{send, Bin} ->
...
{inet_reply, _Sock, Result} ->
...
catch
_:_ ->
send_msg(Sock, Pid)
end.
其目的是循环等待数据,然后进行发送,其使用了try...catch捕获异常.
这段代码有问题么?
对,这段代码的确有问题, 其不是尾递归! try...catch会在stack中保存相应的信息,异常捕获需要放置在函数内部,所以send_msg最后调用的是try...catch,而不是自身,所以不是尾递归!
可以通过代码得到验证:
cat test.erl
-module(test).
-compile([export_all]).
t1() ->
Pid = spawn(fun() -> do_t1() end),
send_msg(Pid, 100000).
t2() ->
Pid = spawn(fun() -> do_t2() end),
send_msg(Pid, 100000).
send_msg(_Pid, 0) ->
ok;
send_msg(Pid, N) ->
Pid ! <<2:(N)>>,
timer:sleep(200),
send_msg(Pid, N-1).
do_t1() ->
erlang:garbage_collect(self()),
Result = erlang:process_info(self(), [memory, garbage_collection]),
io:format("~w ~n", [Result]),
io:format("backtrace:~w~n~n", [erlang:process_display(self(), backtrace)]),
try
receive
_ ->
do_t1()
end
catch
_:_ ->
do_t1()
end.
do_t2() ->
erlang:garbage_collect(self()),
Result = erlang:process_info(self(), [memory, garbage_collection]),
io:format("~w ~n", [Result]),
io:format("backtrace:~w~n~n", [erlang:process_display(self(), backtrace)]),
receive
_ ->
do_t2()
end.
版本1:erlc test.erl && erl -eval "test:t1()"
版本2:erlc test.erl && erl -eval "test:t2()"
你会看到版本1代码的调用堆栈在不断增长,内存也在增长, 而版本2函数调用地址保持不变,内存也没有发生变化!
总结:
1,服务器编程中,循环一定确保为尾递归
2,善于使用OTP,如果使用gen_server替换手写loop,就不会出现这个问题!
本文属转载
xaml页面
<ListBox Name="noteListBox"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<HyperlinkButton Name="noteLocation"
HorizontalContentAlignment="Stretch"
FontSize="42"
VerticalContentAlignment="Stretch"
Content="{Binding noteLocation}"
Click="Mylik_click"
Tag="{Binding filename}"/>
<TextBlock Name="noteCreatetime"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="10"
Text="{Binding noteCreatetime}"
/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
cs页面
//点击日记进行导航
private void Mylik_click(object sender, RoutedEventArgs e)
{
HyperlinkButton likbtn = (HyperlinkButton)sender;
string filename = likbtn.Tag.ToString();
string url = String.Format("/MyNote;component/ViewEdit.xaml?id={0}", filename);
NavigationService.Navigate(new Uri(url, UriKind.Relative));
}
这里主要是想说下点击的时候区别哪个点的
xaml页面
<ProgressBar Height="48"
HorizontalAlignment="Left"
Margin="101,97,0,0"
Name="progressBar1"
VerticalAlignment="Top"
Width="250"
Background="Red"/>
<ProgressBar Height="4"
HorizontalAlignment="Left"
Margin="169,377,0,0"
Name="progressBar2"
VerticalAlignment="Top"
Width="460"
IsIndeterminate="True"/>
这里有两个进度条,第一个在cs设置了委托事件(可以算百分比,边进度边显示),第二个设置了IsIndeterminate="True" 循环进度显示
cs页面
public partial class ProgressBar : PhoneApplicationPage
{
int count = 0;
public ProgressBar()
{
InitializeComponent();
}
//进度的改变事件
private void progressBar1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//获取进度的值 然后进行给title赋值显示
this.PageTitle.Text = e.NewValue.ToString();
}
void dt_Tick(object sender, EventArgs e)
{
//tick一次加一
count++;
//把值赋给进度条
progressBar1.Value = count;
//然后委托进度条的值的改变事件
progressBar1.ValueChanged += new RoutedPropertyChangedEventHandler<double>(progressBar1_ValueChanged);
}
//加载
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
//创建一个时间
DispatcherTimer dt = new DispatcherTimer();
//设置时间间隔100秒
dt.Interval = TimeSpan.FromMilliseconds(100);
//委托事件tick的委托事件
dt.Tick += new EventHandler(dt_Tick);
//启动
dt.Start();
}
}