OTP 15->18升级后,zlib:zip/1引起内存使用升高
来源:互联网 发布:淘宝今日销量 编辑:程序博客网 时间:2024/05/01 10:53
问题
系统原先使用OTP R15B03版本,在升级到OTP 18.0后,CPU下降20%左右(我会告诉你升级就是为了这个么~),但同时发现erlang内存使用比原先高出4倍多。代码、测试环境和用户量均没有变化。通过系统命令发现升级后binary消耗内存显著增加。
升级前,
> erlang:memory(binary).132252416
升级后,
> erlang:memory(binary).487293840
排查
参考Erlang-In-Anger使用recon API recon:bin_leak/1和recon:proc_count/2,并没有发现特定进程占用大量binary的现象。系统启动后不接入用户,并没有观察到内存有显著增长。在接入用户并且用户数量稳定一段时间后,binary内存开销逐渐稳定.
当清空用户后,binary内存回落。故推测内存开销和用户行为相关。
随机抓取用户pid,通过erlang:process_info(Pid, binary)查看用户binary内存开销。发现OTP升级后,用户进程会多出4000 bytes的ref binary开销。
> process_info(pid(0,4398,0),binary).{binary,[{2017145916624,128,3}, {2017145923600,4000,3}, %% <-升级后出现 {2017144817240,176,2} ...]}
系统中每个用户只会处理基本的消息收发,并且消息数据量远远小于4000 bytes,故推测4000 bytes应该是由erlang库函数导致的。搜索erlang源代码,在zlib中有如下
#define DEFAULT_BUFSZ 4000...static ErlDrvData zlib_start(ErlDrvPort port, char* buf){ ZLibData* d; if ((d = (ZLibData*) driver_alloc(sizeof(ZLibData))) == NULL) return ERL_DRV_ERROR_GENERAL; memset(&d->s, 0, sizeof(z_stream)); d->s.zalloc = zlib_alloc; d->s.zfree = zlib_free; d->s.opaque = d; d->s.data_type = Z_BINARY; d->port = port; d->state = ST_NONE; d->bin = NULL; d->binsz = 0; d->binsz_need = DEFAULT_BUFSZ; /* initial buf size for zlib driver*/ d->crc = crc32(0L, Z_NULL, 0); d->inflate_eos_seen = 0; d->want_crc = 0; return (ErlDrvData)d;}
在用户执行的代码里,我们使用zlib对用户数据进行压缩并存储
compress(UState) -> ... zlib:zip(term_to_binary(UData)).
验证后发现在OTP 18.0调用zib:zip/1,即使压缩后实际数据量远小于4000字节,返回数据仍会占用4000字节空间
1> process_info(self(),binary).{binary,[]}2> Bin = list_to_binary(lists:seq(1,100)).<<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,...>>3> process_info(self(),binary). {binary,[{2786449689392,100,3}]}4> CompressB = zlib:zip(Bin). %% <- 调用zlib:zip 对100字节数据进行压缩<<99,100,98,102,97,101,99,231,224,228,226,230,225,229,227, 23,16,20,18,22,17,21,19,151,144,148,146,150,145,...>>5> process_info(self(),binary).{binary,[{2786449689392,100,5},{2786449696784,4000,3}]} %% <- 压缩后会产生4000字节ref binary6> size(CompressB). %% <- 压缩后,实际数据只占用102字节102
解决
压缩数据时,使用term_to_binary(Data, [{compressed, 9}])取代zlib:zip/1,代码修改如下
compress(UState) -> ... term_to_binary(UData),[{compressed, 9}]).
修改后,系统binary内存使用回到升级前。
后记
今儿看了下zip_drv.c的源代码,发现DEFAULT_BUFSZ 4000那个值在R15版本的时候已经存在了。所以升级到OTP 18后的内存增长,应该是erlang zlib:zip/1本身改动引起的。
OTP R15B0367> A = term_to_binary({ckiekd,clowkkd,cldoqpa,difejisdf,iesdifjoe,dkjfe,dkeifjowkkd}).<<131,104,7,100,0,6,99,107,105,101,107,100,100,0,7,99,108, 111,119,107,107,100,100,0,7,99,108,100,111,...>>68> process_info(self(),binary).{binary,[{140271109614736,78,3}]} %% A 78 bytes71> B = zlib:zip(A). %% 再调用zlib:zip对A进行压缩<<107,206,96,79,97,96,75,206,206,76,205,78,73,97,96,79, 206,201,47,207,134,178,82,242,11,11,18,83,24,...>>72> process_info(self(),binary).{binary,[{140271109614736,78,3}, {140271113073160,66,3}]} %% 新生成的B(66 bytes),返回实际大小73> size(B).66
区别在于,新版本调用zlib:zip/1的时候,会返回整个zlip:zip/1压缩使用的buffer。
OTP 18.015> A = term_to_binary({ckiekd,clowkkd,cldoqpa,difejisdf,iesdifjoe,dkjfe,dkeifjowkkd}). <<131,104,7,100,0,6,99,107,105,101,107,100,100,0,7,99,108, 111,119,107,107,100,100,0,7,99,108,100,111,...>>16> process_info(self(),binary).{binary,[{48762496,78,3}]} %% A 78 bytes17> B = zlib:zip(A).<<107,206,96,79,97,96,75,206,206,76,205,78,73,97,96,79, 206,201,47,207,134,178,82,242,11,11,18,83,24,...>>18> size(B).6619> process_info(self(),binary).{binary,[{48762496,78,6}, {133169328,4000,4}]} %% 区别在这里,没有单独为B分配66 bytes的空间
懒得看OTP源码,不清楚是从那个版本引入的,感觉是OTP的一个小bug,回头找他们确认下。
如果一定要使用zlib:zip/1,千万记得调用binary:copy/1,显式地把所需要的结果从4000的buffer里拷贝出来,这样做可以让内存尽快回收,不至于消耗过多内存,如下所示。
OTP 18.031> A = term_to_binary({ckiekd,clowkkd,cldoqpa,difejisdf,iesdifjoe,dkjfe,dkeifjowkkd}). <<131,104,7,100,0,6,99,107,105,101,107,100,100,0,7,99,108, 111,119,107,107,100,100,0,7,99,108,100,111,...>>32> size(A).7833> B = binary:copy(zlib:zip(A)).<<107,206,96,79,97,96,75,206,206,76,205,78,73,97,96,79, 206,201,47,207,134,178,82,242,11,11,18,83,24,...>>34> size(B).6635> process_info(self(),binary).{binary,[{48791552,78,6}, %% A 对应的78bytes {48761696,66,4}, %% 从zlip:zip返回值里copy出来的66 bytes {48761072,2,1}]} %% 这个我就不清楚了….
总之,在使用zlib:zip/1的时候需要格外小心binary内存的开销。
尽量 避免使用这个函数
或者 使用binary:copy/1将返回值强制copy一份
- OTP 15->18升级后,zlib:zip/1引起内存使用升高
- [zlib]_[初级]_[使用Zlib完整解压zip内容]
- 使用 ZLib 压缩/解压 ZIP 文件
- 使用ZLib 压缩/解压缩 zip文件
- 使用 ZLib 压缩/解压 ZIP 文件
- 心得 ~ 使用 zlib库 解压缩 zip文件
- 使用 ZLib 压缩/解压 ZIP 文件
- 使用 ZLib 压缩/解压 ZIP 文件
- 使用 ZLib 压缩/解压 ZIP 文件
- 使用zlib解压标准zip文件
- 使用zlib库解压*.zip文件
- centos 7 中tomcat使用过中重启后内存升高问题。
- top命令引起系统负载升高
- VC中使用zlib压缩目录结构生成zip文件
- Zlib库的使用实现对zip文件的解压缩
- 使用zlib解压.apk/.zip文件(Windows&Ubuntu)
- gzip zip 和zlib
- zlib 解压zip文件
- redhat6.4配置yum
- 汇编指令
- JSP内建对象
- Z-S 变换
- 【Usaco2014 March】Sabotage
- OTP 15->18升级后,zlib:zip/1引起内存使用升高
- 编程小练习8
- 第3周-项目4-换分币
- Oracle 数据库修改索引表空间
- 敏捷宣言
- JavaScript全排列的六种算法 具体实现
- 聚类——层次聚类(Hierarchical Clustering)
- java工程师成神之路
- 逆向 破解简单实战