我正在尝试在包中使用外部指针,但是遇到了一个问题,即好像没有调用终结器并且内存泄漏。
以下是该问题的一个非常人为的示例:
#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}}报告,即使在htop
和rm(list=ls())
之后,R也会使用60-70%的内存。
第二个循环没有明显的内存问题。
我在C端做错什么了吗?我遇到错误了吗?
(Windows上的R版本3.5.2 ubuntu 18.04。)
答案 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将看不到运行垃圾回收的理由。