我自己写的文章居然被别人的博客转载了,然后大言不惭的说作者是他,真是服了。。。
==============================
今天来说一下长期运行的项目会有内存越用越大的情况的发生原因。中所周知,erlang是运行在虚拟机上的,他的GC不是全局的而是针对进程单独GC。所以GC时整个系统不会产生中断,这是他的优点。那么既然有GC为什么还会有内存的增长呢??
当我们运行这个命令(erlang:memory())会发现,内存的增长主要是在binary上。要找到这个原因,就要知道GC里关于binary的处理。
在erlang中,binary的存储位置一共有两处:
1.size<=64 bytes的binary存储在每个进程单独的heap(堆)中。这种bianry叫做Heap-binary。
2.size>64 bytes的binary存储在虚拟机分配出来单独的heap(堆)中,而用到这个binary的进程的heap中只有一个这个binary的引用。这种binary叫做Refc-binaries。
在进程进行GC的时候Heap-binary会随着GC而被释放掉,因为他是只属于这个进程的。相对应的这个进程heap中存储的Refc-binary也会被释放掉,但是其引用的元binary不会被释放,除非所有引用过这个Refc-binary的进程都进行GC后才会被释放。
这里说一下为什么Refc-binaries叫这个名字呢?这个名字全称是Reference-counted-binary,每当有个进程用到这个binary时,除了在自己进程heap中创建这个binary的引用以外,还要把记录这个binary的引用次数+1。只有当这个binary的引用次数为0的时候,才会把这个binary从专门的heap中释放掉。
在我们写erlang代码的时候,很容易遇到这种情况,<<A:8, B:16>> = C.在这里C本身是一个binary,A和B是C的sub-binary,在这个匹配语句中,erlang的VM不会创建2个新的binary,而是对C创建两个引用。这种引用叫做sub-binary。所以在VM中一共有4中binary,分别是:Heap-binary,Heap-binary的sub-binary,Refc-binary,Refc-binary的sub-binary。
而不论是Refc-binary还是Refc-binary的sub-binary,这两种binary都适用刚才说的Refc的GC规则,所以才会导致erlang项目在长期运行后,内存会出现越用越大的情况。具体原因可以举个例子,如果系统中的某个erlang进程起到类似路由的功能,很多Refc-binary的传递都要到这个进程中中转,结果其实这个进程不会操作这些Refc-binary,而由于这个进程基本什么都不干,仅仅中转一下消息所以基本不会有GC的机会,导致了系统中的大量Refc-binary无法得到释放,所以系统内存binary会越用越大。
针对这种情况怎么解决呢?
在创建这些“关键”进程的时候,在选项中加入{fullsweep_after, 0},这个选项的意思是如果有没用的binary,会立马释放掉。
经过了一段时间的研究,发现上面红色字的部分完全说错了,但是我不打算改,而是标注出来,这样可以提醒我自己和大家,以后不太懂的时候别出来装逼了,被发现了就惨了。
下面来说说研究结果:
关于binary的GC有两种:
1.Heap的GC是分代GC。
所谓分代是指数据存储分为young和old两代,最开始所有的Heap-binary都存储在young heap区域,然后当这个young heap空间不足的时候,VM就会对这个进程进行一次浅扫描(minor collection),给当前所有young heap里面的binary的扫描次数+1, 然后把还有用的binary移到新创建的young heap里,把扫描次数大于1的binary放到old heap中,然后删除原有的young heap。为什么这么做呢?因为根据统计学的说法,大部分数据的生存周期都比较短,最新的数据更容易不再被使用。
当old heap空间不足的时候,VM就会对这个进程进行深扫描(major collection),把young&old heap的所有有用的数据放入新的young heap中,然后删除原来的young&old heap。
2.Refc的GC是引用计数的GC。
引用计数的GC上面已经说了,每个用到这个binary的进程都会把这个binary的引用存储在进程自己的Heap空间中,这个引用被叫做ProcBin,然后会给ProcBin在公共Heap里面的Object的计数器+1,进程GC的时候,只要永不到对应的ProcBin就会被GC掉,然后对应的公共Heap里面的Object的计数器-1,当计数器变为0的时候,这个binary被从公共Heap中释放掉。
而{fullsweep_after, Integer}起作用的是对进程自己的Heap binary起作用,Integer的作用是表示,执行Integer次浅扫描后,直接执行深扫描,不用等待old heap满了。
那么这个东西能解决公共Heap区域内存溢出的问题么?
我们假设一个导致内存溢出的情景,还是上面说的那个场景:
如果系统中的某个erlang进程起到类似路由的功能,很多Refc-binary的传递都要到这个进程中中转,结果其实这个进程不会操作这些Refc-binary,而由于这个进程基本什么都不干,仅仅中转一下消息所以基本不会产生新的Heap binary,那么他的私有Heap里面存储的全是ProcBin,结果就是old heap区域怎么也不会满,而VM默认的fullsweep_after是65535,那么假如在65535次浅GC执行完之前很多的Refc-binary把公共Heap区域沾满了,导致没有新的内存可以分配,结果VM由于再接收新的Refc-binary后,没有内存可分配只能挂掉了,这就是传说中的内存泄漏。如果我们给这个路由进程的启动参数设置为{fullsweep_after, 5},也就是5次浅GC后,来一次深GC,那么该进程Heap区域中无用的ProcBin会大幅减少,然后公共Heap区域的内存就会有增有减,保证了整个VM能持续运行。问题解决了! :)
相关推荐
标题中的"erlang-23.2.3-1.el7.x86_64.rpm"和"erlang-23.2.1-1.el7.x86_64.rpm.rar"代表了两个不同的Erlang版本。`.rpm`文件是用于Red Hat Enterprise Linux (RHEL) 和其衍生发行版如CentOS的软件包管理格式,而`....
总结,"erlang-22.3-1.el7.x86_64.rpm"是一个专为RabbitMQ准备的Erlang版本,适合在RHEL 7或类似系统上运行,利用Erlang的并发、分布式和容错特性来保证RabbitMQ的稳定性和效率。通过RPM包的形式,用户可以便捷地在...
esl-erlang_25.0.3-1_centos_7_amd64.rpm erlang_25.0.3-1 rpm用于rabitmq基础环境安装
在标题中的"erlang-18.3-1.el7.centos.x86_64.zip",我们看到的是Erlang的一个特定版本,18.3,针对64位的CentOS 7操作系统(el7)的安装包。这个版本的Erlang包含了必要的组件和服务,使得它能够在CentOS 7上稳定...
erlang-21.3-1.el7.x86_64.rpm rabbitmq基础语言环境。
erlang_23.0.2-1版本 centos7 64bit esl-erlang_23.0.2-1_centos_7_amd64.rpm
erlang-24.0.3-1.el8.x86_64.rpm资源
erlang-24.3.3-1.el9.x86_64.rpm centos
erlang-xmerl-22.3-1.el7.x86_64.rpm,rabbitMQ安装需要依赖此环境。Erlang 是一种多用途编程语言,主要用于开发并发和分布式系统。它最初是一种专有的编程语言,Ericsson 使用它来开发电话和通信应用程序。
标题中的"erlang-19.0.4-1.el7.centos.x86_64.zip"揭示了我们讨论的是Erlang的一个特定版本——19.0.4,该版本针对的是Linux发行版CentOS 7的64位架构。Erlang的版本号通常包括主要版本号、次要版本号和补丁版本号,...
erlang-xmerl-23.0.2-2.el7.x86_64.rpm,rabbitMQ安装需要依赖此环境。Erlang 是一种多用途编程语言,主要用于开发并发和分布式系统。它最初是一种专有的编程语言,Ericsson 使用它来开发电话和通信应用程序。
erlang-19.0.4-1.el7.centos.x86_64.rpm ,官网的正式包
esl-erlang_24.2.1-1_centos_7_amd64.rpm
erlang-20.3-1.el7.centos.x86_64.rpm
erlang-23.2.1-1.el7.x86_64.rpm
erlang-xmerl-23.0.3-1.el7.x86_64.rpm,rabbitMQ安装需要依赖此环境。Erlang 是一种多用途编程语言,主要用于开发并发和分布式系统。它最初是一种专有的编程语言,Ericsson 使用它来开发电话和通信应用程序。
erlang-erts-19.3.6.4-1.el7.x86_64.rpm
esl-erlang_17.3-1~centos~6_amd64.rpm esl-erlang_17.3-1~centos~6_amd64.rpm
Erlang是一种通用的面向并发的编程语言,它由瑞典电信设备制造商爱立信所辖的CS-Lab开发,目的是创造一种可以应对大规模...文件(erlang-23.1.1-1.el7.x86_64.rpm)是Erlang语言是安装包,rpm包可直接在CentOS上安装。