【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/
另,stackoverflow 第七章的範例:http://ishare.edu.sina.com.cn/f/23695419.html






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM