我正在尝试在包中使用外部指针,但遇到一个问题,即好像没有调用终结器并且内存泄漏。
下面是一个非常人为的问题示例:
#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)。
即使使用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将看不到运行垃圾回收的理由。
| 归档时间: |
|
| 查看次数: |
109 次 |
| 最近记录: |