【问题标题】:node.js c++ addon - afraid of memory leaknode.js c++ 插件 - 害怕内存泄漏
【发布时间】:2017-03-23 20:40:52
【问题描述】:

首先,我承认我是 node.jsC++ 插件的新手。

我正在编写我的第一个插件并且我达到了一个很好的结果:插件做我想要的。我复制了我在互联网上找到的各种示例,以在两种语言之间交换复杂的数据,但我对我所写的内容几乎一无所知。

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

所以,在继续编写插件的真正数学之前,我想与社区分享其中的 nanV8 部分如果您发现有问题或可以以更好的方式完成。

感谢大家的帮助,

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)

一年半之后……

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

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

【问题讨论】:

    标签: v8 node.js-addon


    【解决方案1】:

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

    ~MyAddOn() { delete datas; }
    

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

    作为可能存在内存泄漏的示例:您将*callback 指针中保存的对象传递给函数AsyncQueueWorker。然而,您的代码中没有任何地方释放此指针,因此除非队列工作人员为您释放它,否则这里存在内存泄漏。

    您可以使用诸如valgrind 之类的记忆工具来进一步测试您的程序。它会为您发现大多数内存问题,强烈推荐。

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

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

    答案实际上是在循环的inside 中声明变量会更好,只要你能做到。尽可能深入地声明变量,除非你必须重复使用它们。 范围中的变量被限制在最外层的{} 括号中。您可以在this question 中阅读更多相关信息。

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

    本质上,当您这样做时,如果其中一个对象修改了它的“x1K”字符串,那么所有这些对象都会改变。优点是可以释放内存。如果字符串对于所有这些对象都是相同的,则不必存储它的 1,000,000 个副本,您的计算机将只在内存中保留一个,并有 1,000,000 个指向它的指针。如果amd64 下的字符串长度为 9 个 ASCII 字符或更长,那么这相当于显着节省了内存。

    顺便说一句,如果你不打算在声明后修改一个变量,你可以将它声明为const,这是一个常量关键字的缩写,它强制编译器检查你的变量在声明后没有被修改。您可能必须处理相当多的编译器错误,这些错误是关于仅接受它们不修改的东西的非常量版本的函数,其中一些可能不是您自己的代码,在这种情况下,您就不走运了。尽可能保守地使用非常量变量有助于发现问题。

    【讨论】:

    • 感谢您的重播。无论如何,您正在谈论一般 C++ 可能的问题。我想获得有关特定 V8/NaN 可能问题的答案。
    猜你喜欢
    • 2012-03-11
    • 2016-01-01
    • 2015-06-01
    • 2014-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多