博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Erlang 0128] Term sharing in Erlang/OTP 下篇
阅读量:7167 次
发布时间:2019-06-29

本文共 5160 字,大约阅读时间需要 17 分钟。

继续昨天的话题,昨天提到io:format对数据共享的间接影响,如果是下面两种情况恐怕更容易成为"坑", 呃,恰好我都遇到过;

  如果是测试代码是下面这样,得到的结果会是怎样?猜!

1
2
3
4
5
s2()->
  
L=[1,2,3,4,5,6],
  
L2=[L,L,L,L],
erlang:display( {
{erts_debug:size(L),erts_debug:flat_size(L)},{erts_debug:size(L2),erts_debug:flat_size(L2)}}
).

 

结果是

 

1
2
3
5> d:s2().
 
{
{12,12},{56,56}}

  

 

  这个结果出来之后,我足足用了5分钟用来怀疑人生,为什么和期望的结果不一样呢?是因为我现在用的最新版本(17.2)吗?是实现已经修改掉但是没有更新文档吗?出于好奇,我还是按照之前探索问题的套路,生成了一下to_core文件,真相大白:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
's2'
/0 =
    
%% Line 11
    
fun () ->
        
let
<_cor5> =
            
%% Line 14
            
call
'erts_debug'
:
'size'
                
([1|[2|[3|[4|[5|[6]]]]]])
        
in 
let
<_cor4> =
                
%% Line 14
                
call
'erts_debug'
:
'flat_size'
                    
([1|[2|[3|[4|[5|[6]]]]]])
            
in 
let
<_cor3> =
                    
%% Line 14
                    
call
'erts_debug'
:
'size'
                        
([[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]]]]])
                
in 
let
<_cor2> =
                        
%% Line 14
                        
call
'erts_debug'
:
'flat_size'
                            
([[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]|[[1|[2|[3|[4|[5|[6]]]]]]]]]])
                    
in 
%% Line 14
                        
call
'erlang'
:
'display'
                            
({
{_cor5,_cor4},{_cor3,_cor2}})

 

修改一下代码: 

1
2
3
4
s3(L)->
    
L2=[L,L,L,L],
    
{
{erts_debug:size(L),erts_debug:flat_size(L)},{erts_debug:size(L2),erts_debug:flat_size(L2)}}
.

  

对应的s3的代码是

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
's3'
/1 =
    
%% Line 18
    
fun (_cor0) ->
        
let
<L2> =
            
%% Line 19
            
[_cor0|[_cor0|[_cor0|[_cor0|[]]]]]
        
in 
let
<_cor5> =
                
%% Line 20
                
call
'erts_debug'
:
'size'
                    
(_cor0)
            
in 
let
<_cor4> =
                    
%% Line 20
                    
call
'erts_debug'
:
'flat_size'
                        
(_cor0)
                
in 
let
<_cor3> =
                        
%% Line 20
                        
call
'erts_debug'
:
'size'
                            
(L2)
                    
in 
let
<_cor2> =
                            
%% Line 20
                            
call
'erts_debug'
:
'flat_size'
                                
(L2)
                        
in 
%% Line 20
                            
{
{_cor5,_cor4},{_cor3,_cor2}}

  

  换句话,在编译阶段s2方法里面的常量数据就已经展开了,所以L2无论是size还是flat_size都是一样的.之所以要先把这个测试做了,就是避免后面的测试误入陷阱.

 

 这个怎么破呢?除了上面传入参数的方法之外,还有一个路子:换成函数调用即可,如下:

 

1
2
3
4
5
s4()->
  
L=lists:seq(1,6),
  
L2=[L,L,L,L],
erlang:display( {
{erts_debug:size(L),erts_debug:flat_size(L)},{erts_debug:size(L2),erts_debug:flat_size(L2)}}
).

  

 

对应的代码为:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
's4'
/0 =
    
%% Line 24
    
fun () ->
        
let
<L> =
            
%% Line 25
            
call
'lists'
:
'seq'
                
(1, 6)
        
in 
let
<L2> =
                
%% Line 26
                
[L|[L|[L|[L|[]]]]]
            
in 
let
<_cor5> =
                    
%% Line 27
                    
call
'erts_debug'
:
'size'
                        
(L)
                
in 
let
<_cor4> =
                        
%% Line 27
                        
call
'erts_debug'
:
'flat_size'
                            
(L)
                    
in 
let
<_cor3> =
                            
%% Line 27
                            
call
'erts_debug'
:
'size'
                                
(L2)
                        
in 
let
<_cor2> =
                                
%% Line 27
                                
call
'erts_debug'
:
'flat_size'
                                    
(L2)
                            
in 
%% Line 27
                                
call
'erlang'
:
'display'
                                    
({
{_cor5,_cor4},{_cor3,_cor2}})

  

 

 

不要小看这个问题,这样一个常量优化在极端情况下会有"大惊喜",论文里面给了这样一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
show_compiler_crashes() ->
 
L0 = [0],
 
L1 = [L0, L0, L0, L0, L0, L0, L0, L0, L0, L0],
 
L2 = [L1, L1, L1, L1, L1, L1, L1, L1, L1, L1],
 
L3 = [L2, L2, L2, L2, L2, L2, L2, L2, L2, L2],
 
L4 = [L3, L3, L3, L3, L3, L3, L3, L3, L3, L3],
 
L5 = [L4, L4, L4, L4, L4, L4, L4, L4, L4, L4],
 
L6 = [L5, L5, L5, L5, L5, L5, L5, L5, L5, L5],
 
L7 = [L6, L6, L6, L6, L6, L6, L6, L6, L6, L6],
 
L8 = [L7, L7, L7, L7, L7, L7, L7, L7, L7, L7],
 
L9 = [L8, L8, L8, L8, L8, L8, L8, L8, L8, L8],
 
L = [L9, L9, L9, L9, L9, L9, L9, L9, L9, L9],
 
L.

  

影响有多大呢?看结果:
 
After a bit more of 45 minutes of struggling, the compiler tries to allocate 3.7 GB of memory and gives up:

$ erlc demo.erl

Crash dump was written to: erl_crash.dump
eheap_alloc: Cannot allocate 3716993744 bytes of
memory (of type "heap_frag").
Abort

 

  好吧,勇于自黑,由于上面遇到这样让人恼火的问题,我决定在Shell中完成后续的测试,然后,我一脚踏进"新坑":

 

陷阱2 Shell ! Shell !

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Eshell V6.0  (abort with ^G)
1> L=[1,2,3,4,5,6,7,8,9,10].
[1,2,3,4,5,6,7,8,9,10]
2>  L2=[L,L,L,L,L,L].
[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]
3> erts_debug:size(L2).
32
4> erts_debug:flat_size(L2).
132
5> io:format(
"~p"
,[L2]).
[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]ok
6> erts_debug:size(L2).
32
7> erts_debug:flat_size(L2).
132

  

 

    一开始启动shell的时候,Shell的Pid是<0.33.0>.然后我们在中间故意执行一个不存在的方法 fake:fake().这时查看一下,Shell已经重启,Pid变成<0.40.0>.注意再执行erts_debug:size(L2).结果已经变成了132了,换句话说,这里L2数据已经展开了.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Eshell V6.0  (abort with ^G)
1> self().
<0.33.0>
2>  L=[1,2,3,4,5,6,7,8,9,10].
[1,2,3,4,5,6,7,8,9,10]
3>  L2=[L,L,L,L,L,L].
[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]
4>  erts_debug:size(L2).
32
5> erts_debug:flat_size(L2).
132
6> fake:fake().
** exception error: undefined function fake:fake/0
7> self().
<0.40.0>
8>  erts_debug:size(L2).
132
9> erts_debug:flat_size(L2).
132
10>

  

 

   那为什么会触发数据展开(expand ,flattening)呢? 看下面的代码,在Shell启动的时候,会把之前已经绑定的变量作为spawn_link参数以启动新的shell.

 

 

1
2
3
4
5
6
7
erl6.2\lib\stdlib-2.2\src
 
start_eval(Bs, RT, Ds) ->
    
Self = self(),
    
Eval = spawn_link(fun() -> evaluator(Self, Bs, RT, Ds) end),
    
put(evaluator, Eval),
    
Eval.

  

  换句话说,Erlang中使用spawn创建进程,传入的参数(包括函数闭包),需要拷贝到新进程的heap,换句话说进程创建的时候需要考虑参数的大小.

 

  OK,这个问题差不多了,休息.

本文转自博客园坚强2002的博客,原文链接:

如需转载请自行联系原博主。

你可能感兴趣的文章
Linux 用户和组 (4) userdel
查看>>
php面向对象三,继承父类extends
查看>>
网络编程学习——带外数据
查看>>
iOS开发编码建议与编程经验
查看>>
linux文件权限
查看>>
关于Spring事务回滚的问题
查看>>
JRE冲掉了JDK
查看>>
Spring MVC 教程,快速入门,深入分析——关于写几个配置文件的说明
查看>>
我的友情链接
查看>>
MongDB副本集replset测试【MongDB系列二】
查看>>
Android开发之旅:环境搭建及HelloWorld
查看>>
php/web缓存Cache为王
查看>>
化妆品零售变革 电子商务VS传统零售
查看>>
我的友情链接
查看>>
Truncate/Delete/Drop table的特点和区别
查看>>
我的友情链接
查看>>
nginx http core模块学习
查看>>
逢二进一 、逢八进一、逢十六进一
查看>>
搞懂 JAVA 内部类
查看>>
Android中创建与几种解析xml的方法!
查看>>