首页 > 解决方案 > Magick++/NAPI 模块内存泄漏

问题描述

我正在使用利用 Magick++ 库的 node-addon-api 编写本机模块。该模块采用图像的文件路径以及一些参数并返回缓冲区。我似乎遇到了一个非常糟糕的内存泄漏问题,Massif 报告该问题与创建的缓冲区或 Magick++ 图像有关。这是我的 C++ 代码:

#include <napi.h>
#include <list>
#include <Magick++.h>

using namespace std;
using namespace Magick;

class FlipWorker : public Napi::AsyncWorker {
 public:
  FlipWorker(Napi::Function& callback, string in_path, bool flop, string type, int delay)
      : Napi::AsyncWorker(callback), in_path(in_path), flop(flop), type(type), delay(delay) {}
  ~FlipWorker() {}

  void Execute() {
    list<Image> frames;
    list<Image> coalesced;
    list<Image> mid;
    list<Image> result;
    readImages(&frames, in_path);
    coalesceImages(&coalesced, frames.begin(), frames.end());

    for (Image &image : coalesced) {
      flop ? image.flop() : image.flip();
      image.magick(type);
      mid.push_back(image);
    }

    optimizeImageLayers(&result, mid.begin(), mid.end());
    if (delay != 0) for_each(result.begin(), result.end(), animationDelayImage(delay));
    writeImages(result.begin(), result.end(), &blob);
  }

  void OnOK() {
    Callback().Call({Env().Undefined(), Napi::Buffer<char>::Copy(Env(), (char *)blob.data(), blob.length())});
  }

 private:
  string in_path, type;
  bool flop;
  int delay;
  Blob blob;
};

Napi::Value Flip(const Napi::CallbackInfo &info)
{
  Napi::Env env = info.Env();

  Napi::Object obj = info[0].As<Napi::Object>();
  Napi::Function cb = info[1].As<Napi::Function>();
  string path = obj.Get("path").As<Napi::String>().Utf8Value();
  bool flop = obj.Has("flop") ? obj.Get("flop").As<Napi::Boolean>().Value() : false;
  string type = obj.Get("type").As<Napi::String>().Utf8Value();
  int delay = obj.Get("delay").As<Napi::Number>().Int32Value();

  FlipWorker* flipWorker = new FlipWorker(cb, path, flop, type, delay);
  flipWorker->Queue();
  return env.Undefined();
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "flip"), Napi::Function::New(env, Flip));
  return exports;
}

NODE_API_MODULE(addon, Init);

还有一个示例 JS 脚本:

const image = require("./build/Release/image.node");

setInterval(() => {
  image.flip({ path: "/home/esm/animated.gif", type: "gif", delay: 0 }, (error, buffer) => {
    console.log(buffer);
    console.log(process.memoryUsage().rss);
  });
}, 10000);

这是脚本的示例输出:

<Buffer 47 49 46 38 39 61 80 02 66 01 f7 00 00 38 44 3a 62 58 26 70 64 27 12 1c 4d 19 26 50 26 30 57 10 38 79 2c 37 67 35 51 57 14 47 79 35 4a 71 55 4f 4f 68 ... 868294 more bytes>
69496832
<Buffer 47 49 46 38 39 61 80 02 66 01 f7 00 00 38 44 3a 62 58 26 70 64 27 12 1c 4d 19 26 50 26 30 57 10 38 79 2c 37 67 35 51 57 14 47 79 35 4a 71 55 4f 4f 68 ... 868294 more bytes>
110673920
<Buffer 47 49 46 38 39 61 80 02 66 01 f7 00 00 38 44 3a 62 58 26 70 64 27 12 1c 4d 19 26 50 26 30 57 10 38 79 2c 37 67 35 51 57 14 47 79 35 4a 71 55 4f 4f 68 ... 868294 more bytes>
152092672
<Buffer 47 49 46 38 39 61 80 02 66 01 f7 00 00 38 44 3a 62 58 26 70 64 27 12 1c 4d 19 26 50 26 30 57 10 38 79 2c 37 67 35 51 57 14 47 79 35 4a 71 55 4f 4f 68 ... 868294 more bytes>
192970752
<Buffer 47 49 46 38 39 61 80 02 66 01 f7 00 00 38 44 3a 62 58 26 70 64 27 12 1c 4d 19 26 50 26 30 57 10 38 79 2c 37 67 35 51 57 14 47 79 35 4a 71 55 4f 4f 68 ... 868294 more bytes>
204517376

如您所见,每次运行该函数时,驻留集的大小都会显着增加。我使用的任何格式的每个图像都会发生这种情况。我将如何防止代码泄漏?提前致谢。

编辑: 我做了一些更多的挖掘,结果发现由于缓冲区不是通过 JS 创建的,所以它不符合垃圾收集的条件。我现在想知道是否可以创建一个缓冲区,让 V8 收集垃圾并仍然提供相同的数据。

标签: c++node.jsv8magick++node-addon-api

解决方案


推荐阅读