node.js c ++ addon - 怕内存泄漏

时间:2017-03-23 20:40:53

标签: v8 node.js-addon

首先,我承认我是 c ++ 插件的新手 node.js

我正在写我的第一个插件,并且我得到了一个好结果:插件做了我想要的。我从互联网上发现的各种例子中复制了两种语言之间的复杂数据,但我对所写内容几乎一无所知。

让我害怕的第一件事是我没有写任何东西似乎释放了一些记忆;令我严重担心的另一件事是,我不知道我写的内容是否会对 V8 垃圾收集器造成帮助或造成混淆;顺便说一句,我不知道是否有更好的方法来做我做的事情(在 C ++ 中迭代 js对象 ,在 C ++ 中创建 js Object ,在 C ++ 中创建字符串以用作属性 of js Object s以及我在代码中可以找到的其他错误。)

所以,在继续写我的插件的真实数学工作之前,我想与社区分享 nan V8 部分来询问如果你看到错误或者可以更好地完成。

谢谢大家的帮助,

ICC

#include <map>
#include <nan.h>

using v8::Array;
using v8::Function;
using v8::FunctionTemplate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Value;
using v8::String;
using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::GetFunction;
using Nan::HandleScope;
using Nan::New;
using Nan::Null;
using Nan::Set;
using Nan::To;

using namespace std;

class Data {
public:
    int dt1;
    int dt2;
    int dt3;
    int dt4;
};

class Result {
public:
    int   x1;
    int   x2;
};

class Stats {
public:
    int stat1;
    int stat2;
};

typedef map<int, Data>    DataSet;
typedef map<int, DataSet> DataMap;

typedef map<float, Result>    ResultSet;
typedef map<int,   ResultSet> ResultMap;

class MyAddOn: public AsyncWorker {
private:
    DataMap   *datas;
    ResultMap  results;
    Stats      stats;

public:
    MyAddOn(Callback *callback, DataMap *set): AsyncWorker(callback), datas(set) {}
    ~MyAddOn() { delete datas; }

    void Execute () {
        for(DataMap::iterator i = datas->begin(); i != datas->end(); ++i) {
            int      res   =  i->first;
            DataSet *datas = &i->second;

            for(DataSet::iterator l = datas->begin(); l != datas->end(); ++l) {
                int   dt4  =  l->first;
                Data *data = &l->second;

                // TODO: real population of stats and result
            }

            // test result population
            results[res][res].x1 = res;
            results[res][res].x2 = res;
        }

        // test stats population
        stats.stat1 = 23;
        stats.stat2 = 42;
    }

    void HandleOKCallback () {
        Local<Object> obj;
        Local<Object> res  = New<Object>();
        Local<Array>  rslt = New<Array>();
        Local<Object> sts  = New<Object>();
        Local<String> x1K  = New<String>("x1").ToLocalChecked();
        Local<String> x2K  = New<String>("x2").ToLocalChecked();
        uint32_t      idx  = 0;

        for(ResultMap::iterator i = results.begin(); i != results.end(); ++i) {
            ResultSet *set = &i->second;

            for(ResultSet::iterator l = set->begin(); l != set->end(); ++l) {
                Result *result = &l->second;

                // is it ok to declare obj just once outside the cycles?
                obj = New<Object>();

                // is it ok to use same x1K and x2K instances for all objects?
                Set(obj, x1K, New<Number>(result->x1));
                Set(obj, x2K, New<Number>(result->x2));
                Set(rslt, idx++, obj);
            }
        }

        Set(sts, New<String>("stat1").ToLocalChecked(), New<Number>(stats.stat1));
        Set(sts, New<String>("stat2").ToLocalChecked(), New<Number>(stats.stat2));

        Set(res, New<String>("result").ToLocalChecked(), rslt);
        Set(res, New<String>("stats" ).ToLocalChecked(), sts);

        Local<Value> argv[] = { Null(), res };

        callback->Call(2, argv);
    }
};

NAN_METHOD(AddOn) {
    Local<Object> datas    = info[0].As<Object>();
    Callback     *callback = new Callback(info[1].As<Function>());
    Local<Array>  props    = datas->GetOwnPropertyNames();
    Local<String> dt1K     = Nan::New("dt1").ToLocalChecked();
    Local<String> dt2K     = Nan::New("dt2").ToLocalChecked();
    Local<String> dt3K     = Nan::New("dt3").ToLocalChecked();
    Local<Array>  props2;
    Local<Value>  key;
    Local<Object> value;
    Local<Object> data;
    DataMap      *set      = new DataMap();
    int           res;
    int           dt4;
    DataSet      *dts;
    Data         *dt;

    for(uint32_t i = 0; i < props->Length(); i++) {
        // is it ok to declare key, value, props2 and res just once outside the cycle?
        key    = props->Get(i);
        value  = datas->Get(key)->ToObject();
        props2 = value->GetOwnPropertyNames();
        res    = To<int>(key).FromJust();
        dts    = &((*set)[res]);

        for(uint32_t l = 0; l < props2->Length(); l++) {
            // is it ok to declare key, data and dt4 just once outside the cycles?
            key  = props2->Get(l);
            data = value->Get(key)->ToObject();
            dt4  = To<int>(key).FromJust();
            dt   = &((*dts)[dt4]);

            int dt1 = To<int>(data->Get(dt1K)).FromJust();
            int dt2 = To<int>(data->Get(dt2K)).FromJust();
            int dt3 = To<int>(data->Get(dt3K)).FromJust();

            dt->dt1 = dt1;
            dt->dt2 = dt2;
            dt->dt3 = dt3;
            dt->dt4 = dt4;
        }
    }

    AsyncQueueWorker(new MyAddOn(callback, set));
}

NAN_MODULE_INIT(Init) {
    Set(target, New<String>("myaddon").ToLocalChecked(), GetFunction(New<FunctionTemplate>(AddOn)).ToLocalChecked());
}

NODE_MODULE(myaddon, Init)

一年半之后......

如果有人感兴趣,我的服务器已启动并运行,因为我的问题及其所需的内存量是稳定的。

我不能说我写的代码是否真的没有内存泄漏,或者在每个线程执行结束时是否释放了丢失的内存,但是如果你像我一样害怕,我可以说使用相同的结构并且电话不会引起任何实际问题。

1 个答案:

答案 0 :(得分:0)

你实际上使用代码行释放了一些你使用的内存:

~MyAddOn() { delete datas; }

从本质上讲,C ++内存管理归结为始终为delete创建的每个对象调用new。还有许多其他特定于体系结构和传统的“C”内存管理功能,但是当您不需要性能优势时,并不一定要使用这些功能。

作为可能存在内存泄漏的示例:您将*callback指针中保存的对象传递给函数AsyncQueueWorker。但是在你的代码中没有任何地方释放这个指针,所以除非Queue worker为你释放它,否则这里会有内存泄漏。

您可以使用valgrind等内存工具进一步测试您的程序。它会为您发现大多数内存问题,强烈建议。

我观察到的一件事是你经常问(转述):

  

在我的循环之外声明X是否可以?

答案实际上是在你的循环中声明变量 更好,只要你能做到。尽可能将变量声明为内部,除非您必须重复使用它们。变量在范围中被限制在最外面的{}括号中。您可以在this question中详细了解相关信息。

  

可以为所有对象使用相同的x1K和x2K实例吗?

本质上,当你这样做时,如果其中一个对象修改了它的'x1K'字符串,那么它将为所有这些字符串改变。优点是你可以释放内存。如果所有这些对象的字符串都是相同的,而不是必须存储1,000,000个拷贝,那么你的计算机只会在内存中保留一个,而是拥有1,000,000个指针。如果字符串在amd64下长度为9个ASCII字符,那么这相当于节省了大量内存。

顺便说一下,如果你不打算在声明之后修改变量,你可以将它声明为const,这是一个常量的关键字,它强制编译器在你之后检查你的变量是否被修改宣言。你可能不得不处理很多关于只接受他们不修改的非const版本的函数的编译器错误,其中一些可能不是你自己的代码,在这种情况下你运气不好。尽可能保守非常量变量可以帮助发现问题。