使用C外部指针的R内存泄漏

thc*_*thc 4 r rcpp

我正在尝试在包中使用外部指针,但遇到一个问题,即好像没有调用终结器并且内存泄漏。

下面是一个非常人为的问题示例:

#include <Rcpp.h>
using namespace Rcpp;

void finalize(SEXP xp){
  delete static_cast< std::vector<double> *>(R_ExternalPtrAddr(xp));
}

// [[Rcpp::export]]
SEXP ext_ref_ex() {
  std::vector<double> * x  = new std::vector<double>(1000000);
  SEXP xp = PROTECT(R_MakeExternalPtr(x, R_NilValue, R_NilValue));
  R_RegisterCFinalizer(xp, finalize);
  UNPROTECT(1);
  return xp;
}
Run Code Online (Sandbox Code Playgroud)

R面:

library(Rcpp)
sourceCpp("tests.cpp")

# breaks and/or crashes
for(i in 1:10000) {
  z <- ext_ref_ex()
}

# no issue
for(i in 1:10000) {
  z <- ext_ref_ex()
  rm(z)
  gc()
}
Run Code Online (Sandbox Code Playgroud)

运行第一个循环,R会在进行足够的迭代(问题1)后最终发生段错误,而预期的行为是应清理数据且不应出现段错误。

问题2是,如果您中断进程并调用gc(),有时内存将被清除,但通常不会清除。根据该htop报告,即使在rm(list=ls())和之后,R也会使用60-70%的内存gc()。

第二个循环没有明显的内存问题。

我在C端做错了吗?我遇到错误了吗?

(Windows上的R版本3.5.2 ubuntu 18.04)。

Ral*_*ner 5

即使使用Rcpp代替C API创建外部指针并注册终结器,我也可以重现该问题:

#include <Rcpp.h>

// [[Rcpp::export]]
Rcpp::XPtr< std::vector<double> > ext_ref_ex() {
  std::vector<double> * x  = new std::vector<double>(1000000);
  Rcpp::XPtr< std::vector<double> > xp(x, true) ;
  return xp;
}
Run Code Online (Sandbox Code Playgroud)

对我来说,仅包含gc()循环就足以解决问题:

for (i in 1:10000) {
  z <- ext_ref_ex()
  gc() # crash without this line
}
Run Code Online (Sandbox Code Playgroud)

因此,“ finalizer未运行”似乎不是问题,而是“垃圾收集未运行”。我的解释:您正在为分配大量内存,vector而为外部指针分配了少量内存。R只知道外部指针。因此,如果超出范围,R将看不到运行垃圾回收的理由。

  • ...而R则是设计使然-外部指针的作用是“超出R”,并且不受其影响。因此,如果OP在那里分配并且没有释放,那么在他/她的端头就是一个简单的设计错误。 (2认同)