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

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

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)}}
).

结果是

5> d:s2().

{{12,12},{56,56}}

  

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

'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}})

修改一下代码:

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的代码是

'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都是一样的.之所以要先把这个测试做了,就是避免后面的测试误入陷阱.

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

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)}}
).

  

对应的代码为:

'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}})

  

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

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 !

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数据已经展开了.

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.

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,这个问题差不多了,休息.

[Erlang 0128] Term sharing in Erlang/OTP 下篇的更多相关文章

  1. [Erlang 0127] Term sharing in Erlang/OTP 上篇

    之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文: On Preserving Term Sharing in the Erlang Virtual Machine ...

  2. [Erlang 0121] 当我们谈论Erlang Maps时,我们谈论什么 Part 3

    Erlang/OTP 17.0 has been released  http://www.erlang.org/download/otp_src_17.0.readme     Erlang/OTP ...

  3. [Erlang 0116] 当我们谈论Erlang Maps时,我们谈论什么 Part 1

         Erlang 增加 Maps数据类型并不是很突然,因为这个提议已经进行了2~3年之久,只不过Joe Armstrong老爷子最近一篇文章Big changes to Erlang掀起不小了风 ...

  4. Erlang基础 -- 介绍 -- 历史及Erlang并发

    前言 最近在总结一些Erlang编程语言的基础知识,拟系统的介绍Erlang编程语言,从基础到进阶,然后再做Erlang编程语言有意思的库的分析. 其实,还是希望越来越多的人关注Erlang,使用Er ...

  5. [Erlang 0117] 当我们谈论Erlang Maps时,我们谈论什么 Part 2

    声明:本文讨论的Erlang Maps是基于17.0-rc2,时间2014-3-4.后续Maps可能会出现语法或函数API上的有所调整,特此说明. 前情提要: [Erlang 0116] 当我们谈论E ...

  6. 学习:erlang的term反序列化,string转换为term

    一. string_to_term(String) ->    case erl_scan:string(String++".") of        {ok, Tokens ...

  7. [Erlang 0125] Know a little Erlang opcode

    Erlang源代码编译为beam文件,代码要经过一系列的过程(见下面的简图),Core Erlang之前已经简单介绍过了Core Erlang,代码转换为Core Erlang,就容易拨开一些语法糖的 ...

  8. [Erlang 0109] From Elixir to Erlang Code

    Elixir代码最终编译成为erlang代码,这个过程是怎样的?本文通过一个小测试做下探索.         编译一旦完成,你就看到了真相   Elixir代码组织方式一方面和Erlang一样才用非常 ...

  9. [Erlang 0119] Erlang OTP 源码阅读指引

      上周Erlang讨论群里面提到lists的++实现,争论大多基于猜测,其实打开代码看一下就都明了.贴出代码截图后有同学问这代码是哪里找的?   "代码去哪里找?",关于Erla ...

随机推荐

  1. 在rem布局下使用背景图片以及sprite

    现在移动端页面用rem布局已经是一大流派了,成熟的框架如淘宝的flexiable.js,以及我的好友@墨尘写的更轻量级的hotcss.用rem作单位使得元素能够自适应后,还有一块需要关注的,那就是背景 ...

  2. 如何在VMware中安装Windows Phone SDK 8.0 (支持模拟器调试)

    相信很多开发者目前的系统还是Win7或Mac,一般不会为了开发某个程序而重装系统,所以我们就需要用到VMware这类的虚拟机来模拟预期的开发环境.在开始介绍前,给大家说明下我当前的软硬件环境,本文所讲 ...

  3. Entity Framework 6 Recipes 2nd Edition(11-11)译 -> 在LINQ中调用数据库函数

    11-11. 在LINQ中调用数据库函数 问题 相要在一个LINQ 查询中调用数据库函数. 解决方案 假设有一个任命(Appointment )实体模型,如Figure 11-11.所示, 我们想要查 ...

  4. 【Win 10 应用开发】文件读写的三种方案

    本文老周就跟伙伴们探讨一下关于文件读写的方法.总得来说嘛,有三种方案可以用,而且每种方案都各有特色,也说不上哪种较好.反正你得记住老祖宗留给我们的大智慧——事无定法,灵活运用者为上. OK,咱们开始吧 ...

  5. Java Collection开发技巧

    Java Collection(集合) 集合中的一些技巧: 通过Collections类的静态方法,可以对集合进行一些操作 1 java.util.List<Integer> number ...

  6. 如何在删除ibdata1和ib_logfile的情况下恢复MySQL数据库

    昨天,有个朋友对公司内部使用的一个MySQL实例开启binlog,但是在启动的过程中失败了(他也没提,为何会失败),在启动失败后,他删除了ibdata1和ib_logfile,后来,能正常启动了,但所 ...

  7. parseInt实例详解

    parseInt() 函数可解析一个字符串,并返回一个整数. parseInt(string, radix) 参数 描述 string 必需.要被解析的字符串. radix 可选.表示要解析的数字的基 ...

  8. angularjs和ajax的结合使用 (二)

    今天我们来继续丰富上次的例子.我们来搞些 稍微复杂点的应用. 首先我们来加一个全选 的功能. 上一篇的例子里我们看到 分页时载入的是我们通过linq 查询自定义列 然后构建的匿名类 .使用这种EF框架 ...

  9. JavaScript RegExp 基础详谈

    前言: 正则对于一个码农来说是最基础的了,而且在博客园中,发表关于讲解正则表达式的技术文章,更是数不胜数,各有各的优点,但是就是这种很基础的东西,如果我们不去真正仔细研究.学习.掌握,而是抱着需要的时 ...

  10. .Net语言 APP开发平台——Smobiler学习日志:Poplist控件的正确打开方式以及如何快速实现

    最前面的话:Smobiler是一个在VS环境中使用.Net语言来开发APP的开发平台,也许比Xamarin更方便 样式一 一.目标样式 我们要实现上图中的效果,需要如下的操作: 1.从工具栏上的&qu ...