【R笔记】R的内存管理和垃圾清理

笔记:
1、R输入命令时速度不要太快,终究是个统计软件,不是编程!
2、memory.limit()查看当前操作系统分配内存给R的最大限度(单位是M?)
3、要经常 rm(object) 或者 rm(list=ls()) 和 gc()释放内存空间
4、尽量提前设置矩阵大小固定不变,矩阵每增长一次,即使赋给同名的变量,都需要新开辟一块更大的空间
5、尽量避免循环语句
6、用矩阵,而不是数据框
7、在大数据集运行前现在子数据集上测试程序
8、将数据保存到R的住内存之外的包:

    biglm和speedglm包能以高效的形式实现大型数据的线性模型拟合和广义线性模型拟合
    分析bigmemory包生成的矩阵:
  • biganalytics包提供k均值聚类、列统计和一个biglm的封装
  • bigtabulate提供了table()\split()和tapply()功能
  • bigalgebra包提供了高级的线性代数函数
    biglar包跟ff配合使用,为在内存中无法放至的大数据提供了最小角回归,lasso和逐步回归分析。
    Borbdingnag包可以处理大数字(大于2的1024次方的数)

  写R程序的人,相信都会遇到过“cannot allocate vector of size”或者“无法分配大小为...的矢量”这样的错误。原因很简单,基本都是产生一个大矩阵等对象时发生的,最干脆的解决办法有两种,第一种是加大内存换64位系统,第二种是改变算法避免如此大的对象。第一种办法,是最好的办法,不过大对象的需求是没有止尽的,终究不是长久之道。第二种办法是最好的思路,无论多么大的对象都是可以弄小的,无非就是分而治之、时间换空间等,对算法的研究也是没有止尽的。

  升级硬件和改进算法是解决内存问题的永恒的办法,超出了本文想要表述的范围。在这里,只是简单谈谈R语言的内存管理和垃圾清理机制,只有对这些有所了解,才能对任何问题都能找到针对性的解决办法。

  相信所有人在遇到无法分配矢量这一问题后,都能很快地找到改变“--max-mem-size”(假设都是在Windows下)或者“memory.limit”的方法,的确,这是最直接的方法。因为出现新对象无法分配内存的直接原因就是内存不够,R获取内存的方式和其他应用程序一样,都是向操作系统要内存,如果无法获取连续的某个大小的内存空间,就会出现无法分配内存的错误。由于大家使用R时通常都是自动安装自动运行,操作系统愿意分配给R多少内存都是采用的默认设置,在R中使用命令memory.size(NA)或者memory.limit()可以看到当前设置下操作系统能分配给R的最大内存是多少。同时可以使用memory.size(F)查看当前R已使用的内存,memory.size(T)查看已分配的内存(注意刚开始时已使用内存和已分配内存是同步增加的,但是随着R中的垃圾被清理,已使用内存会减少,而已分配给R的内存一般不会改变。)。如果memory.limit()得到的数是一个很小的内存,说明操作系统太小气了,留那么多内存给别的程序用不给R。解决办法很简单,就是打开R时不通过双击图标,而是在“运行”中输入“Rgui --max-mem-size 2Gb”(假设要分配2G内存且在环境变量中正确设置了R的安装文件夹),在运行memory.limit()就会发现内存加大了,其实更简单的方法是直接在R中运行memory.limit(2000),效果一模一样,而且不用重启R。

  可惜大多数情况下改变这个值也不会有效果,因为这个值已经足够大,那么无法分配内存的原因不是操作系统小气对R不公,而是它确实拿不出来,谁找它要也拿不出来。这个时候就需要了解R的内存管理到底是怎么回事了。

  R的操作基本都是通过变量来实现的,变量可以是各种各样的对象类型,R中的对象(比如矩阵)在内存中存于两种不同的地方,一种是堆内存(heap),其基本单元是“Vcells”,每个大小为8字节,新来一个对象就会申请一块空间,把值全部存在这里,和C里面的堆内存很像。第二种是地址对(cons cells),和LISP里的cons cells道理一样,主要用来存储地址信息,最小单元一般在32位系统中是28字节、64位系统中是56字节。在R中,可以通过ls()来查看当前所有对象名,对于每一个对象,可以通过object.size(x)来查看其占用内存的大小。

  如果是因为当前对象占用内存过多,那么可以通过处理对象来获取更大的可用内存。一个很有用的方法是改变对象的存储模式,通过storage.mode(x)可以看到某个对象的存储模式,比如某个矩阵默认就是“double”的,如果这个矩阵的数值都是整数甚至0-1,完全没必要使用double来占用空间,可以使用storage.mode(x) <- "integer"将其改为整数型,可以看到该对象的大小会变为原来的一半。

  对于当前对象占用内存过多的情况,一个很主要的原因就是在写程序的过程中造成了太多的中间对象,R是一个很方便的语言,大家使用它一般都是写各种复杂的模型和算法,很多问题构造几个矩阵经过一系列的矩阵运算就可以很快解决,但是这些辅助算法的大矩阵如果不清理,就会留在系统中占内存。因此在写程序中对于中间对象,经常使用rm(x)是一个很好的习惯,如果是非常重要的信息不想删掉,可以存在硬盘里,比如csv文件或者RSqlite等。

  rm()用来删除对象时,只会删除变量的引用,并不会立即清除占用的内存空间,失去引用的对象就成了内存中的垃圾,R清理垃圾的机制和JAVA很像,都是在一定时间内自动发现垃圾再集中清理。所以通过rm()删除对象后在Windows的任务管理器可以看到R进程占用的内存并没有被立即释放,而是过一段时间后才会清理。如果想要删除的对象立刻被清理,可以运行垃圾处理函数gc(),将会立刻释放空间。但是通常不是很必要,因为当内存不够时系统会自动清理垃圾的,我们要做的只是将不再使用的对象rm()掉,在写R程序时应该养成习惯。

  很多时候,在程序中尤其是循环里,如果内存处理不当,还没来得及垃圾清理,就会把内存撑爆,因此新建对象时一定要考虑到R的内存管理机制。大家都知道R中矩阵的维度并不需要赋一个固定的值(很多语言的数组长度不能为变量),这为写程序带来了极大的方便,因此经常在循环中会出现某个矩阵越来越长的情况,实际上,矩阵每增长一次,即使赋给同名的变量,都需要新开辟一块更大的空间,假设初始矩阵为100K,第二个为101K,一直增到120K,那么,将会分别开辟100K、101K一直到120K的连续堆内存,如果一开始就开一块120K的,使之从101K逐渐增长到120K,将会大大地节约内存。cbind函数也是这个道理,所以在循环中要注意不要滥用。

  要处理好内存的问题其实很简单,养成随时关注内存的习惯即可,每新建一个对象或者循环赋值的时候适当估算一下所占内存,大内存的中间变量用完后记得清理。如果实在需要新建一个巨大的对象,那么就该考虑一些专门处理大内存对象以及并行处理的包,比如bigmemory等


 
除了 Hadoop 以外,似乎 colbycol 包是方案之一:http://colbycol.r-forge.r-project.org/
另,* 第七章的範例:http://ishare.edu.sina.com.cn/f/23695419.html

上一篇:JavaScript引擎浅析


下一篇:浏览器-09 javascript引擎和Chromium网络栈