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

时间:2019-03-19 07:56:20

标签: 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;
}

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()
}

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

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

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

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

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

1 个答案:

答案 0 :(得分: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;
}

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

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

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