Утечка памяти R с использованием внешних указателей C - PullRequest
4 голосов
/ 19 марта 2019

Я пытаюсь использовать внешние указатели в пакете, но я столкнулся с проблемой, когда казалось, что финализатор не вызывается, и память просочилась.

Ниже приведен чрезвычайно надуманный пример проблемы:

#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(), иногда память будет очищена, но обычно это не так.Согласно отчету htop, R использует 60-70% памяти, даже после rm(list=ls()) и gc().

Во втором цикле нет проблем с памятью.

Я что-то не так делаю на стороне C?Я сталкиваюсь с ошибкой?

(R версия 3.5.2 Ubuntu 18.04 для Windows.)

1 Ответ

5 голосов
/ 19 марта 2019

Я могу воспроизвести проблему даже при использовании 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
}

Так что, похоже, проблема не в том, что «финализатор не работает», а в «сборке мусора не работает». Моя интерпретация: Вы выделяете много памяти для vector и немного памяти для внешнего указателя. R знает только о внешнем указателе. Так что, если это выходит за рамки, R не видит причин для запуска сборки мусора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...