回复 第8楼 的 earclimate:回复 第9楼 的 superdesolator:内存回收机制。函数或者什么东西创建的临时变量被释放后,R不会马上调用内存回收g(垃圾)c(回收)函数,所以有时候看windows的任务管理器/Linux的top不能看出R内存变化。R会在内存不够用(要去读C代码)时自动调用gc释放内存。这一点和JAVA类似。这一点和编译语言C/Cpp有非常大的区别,后者要用户手动free或者析构(class会自主调用析构函数,但是前提要是有的话)。
麻烦的问题:
1. R的变量由于大量使用了范型或者干脆就像Lisp那样啥都能放进去(Vcells专门指向量,Ncells指其它所有,我们一般看到的时两者之和),这样导致了一个很严重的问题
<br />
> gc()<br />
used (Mb) gc trigger (Mb) max used (Mb)<br />
Ncells 185002 9.9 407500 21.8 323586 17.3<br />
Vcells 283238 2.2 786432 6.0 786432 6.0<br />
> f<-function(){<br />
+ a=sample(1:1E6);<br />
+ return (function(...)a)<br />
+ }<br />
> y=f();<br />
> gc();<br />
used (Mb) gc trigger (Mb) max used (Mb)<br />
Ncells 185227 9.9 407500 21.8 323586 17.3<br />
Vcells 783876 6.0 2643203 20.2 2523873 19.3<br />
</p>
这样的代码是不会处理掉所谓的临时变量a的. 这也是为什么R的ext文档有专门写防止内存泄漏的问题。试想在MCMC中引入这么个玩意儿,有点可怕啊。
2. 引用(不是指针!)
引用类似于制造拷贝,但是并不是完全拷贝下来。这点和C++和JAVA一样。
<br />
a <- 1:10<br />
a[1] <- a[1]+1<br />
</p>
这里的计算并不会真正做一个a的完整拷贝,只是建立了个a[1]的引用罢了。(但是我可以吐槽R没有 a+=1这种操作么?)
但是R有独有的玩意儿:environment. 变量的作用域简直就是地狱,使用起来都有可能不知道自己到底在用那个变量何况清理起来。比如下面这种东西
<br />
a=100<br />
f<-function(...){<br />
a<br />
}<br />
f()<br />
a=10000<br />
f()<br />
</p>
结果你猜是什么?这种东西写出来不好理解,内存清理的时候更加是灾难。同理还有下面这样的事情
<br />
a<-list();<br />
for(i in 1:10000) a[[i]]=sample(1:1E10,i)<br />
</p>
这种糟糕的事情想想也只能R这样编码自由的语言才有--当然还有matlab,但是matlab会在你试图这么做的时候(编辑代码的时候哦)给你个大warning。
3. data.frame
我首先要说明一点,R不是C/Cpp,所以物理指针这种玩意儿不要太在意(参见pryr包)。但至少list,vector和matrix一旦大小确定了物理地址就确定了。但是data.frame不会(其名字空间还是hush的奇葩东西)。至于占用内存大小,看看下面的例子
<br />
> xx<-matrix(0,10000000,10)<br />
> object.size(data.frame(xx))<br />
Error: cannot allocate vector of size 76.3 Mb<br />
> object.size(xx)<br />
[1] 800000112<br />
有点意思吧。</p>
该怎么做:
一般写着玩的话,一般不太容易碰到内存泄漏,所以R自身会触发gc triger;写大型程序和包的时候有memory profiling工具可以用,比如lineprof包(github上有),还有个很漂亮的GUI显示(shiny貌似叫)。当然最关键的是
1. 能上Rcpp就Rcpp吧--如果你对你的C有信心;
2. 养成良好的编程习惯--我说的不是写注释,那太初等,而是不要写出我上面举例的东西出来。