国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

memwatch分析

worldligang / 3653人閱讀

摘要:介紹是一個擴(kuò)展,主要用來觀察內(nèi)存泄露問題,基本用法如下實現(xiàn)分析分析的版本為。的入口函數(shù)在文件中,通過進(jìn)行聲明。下面分析下函數(shù)的具體實現(xiàn)函數(shù)內(nèi)部會遞歸調(diào)用函數(shù)得到最終堆快照的結(jié)果。回調(diào)的觸發(fā)與的鉤子有關(guān),詳細(xì)看下一節(jié)分析。

介紹

memwatch是一個c++擴(kuò)展,主要用來觀察nodejs內(nèi)存泄露問題,基本用法如下:

const memwatch = require("@airbnb/memwatch");
function LeakingClass() {
}

memwatch.gc();
var arr = [];
var hd = new memwatch.HeapDiff();
for (var i = 0; i < 10000; i++) arr.push(new LeakingClass);
var hde = hd.end();
console.log(JSON.stringify(hde, null, 2));
實現(xiàn)分析

分析的版本為@airbnb/memwatch。首先從binding.gyp開始入手:

{
  "targets": [
    {
      "target_name": "memwatch",
      "include_dirs": [
        "

這份配置表示其生成的目標(biāo)是memwatch.node,源碼是src目錄下的heapdiff.ccinit.ccmemwatch.ccutil.cc,在項目編譯的過程中還需要include額外的nan目錄,nan目錄通過執(zhí)行node -e "require("nan")按照node模塊系統(tǒng)尋找nan依賴, 表示后面是一條指令。

memwatch的入口函數(shù)在init.cc文件中,通過NODE_MODULE(memwatch, init);進(jìn)行聲明。當(dāng)執(zhí)行require("@airbnb/memwatch")的時候會首先調(diào)用init函數(shù):

void init (v8::Handle target)
{
    Nan::HandleScope scope;
    heapdiff::HeapDiff::Initialize(target);

    Nan::SetMethod(target, "upon_gc", memwatch::upon_gc);
    Nan::SetMethod(target, "gc", memwatch::trigger_gc);

    Nan::AddGCPrologueCallback(memwatch::before_gc);
    Nan::AddGCEpilogueCallback(memwatch::after_gc);
}

init函數(shù)的入口參數(shù)v8:Handle target可以類比nodejs中的module.exportsexports對象。函數(shù)內(nèi)部做的實現(xiàn)可以分為三塊,初始化target、給target綁定upon_gcgc兩個函數(shù)、在nodejs的gc前后分別掛上對應(yīng)的鉤子函數(shù)。

Initialize實現(xiàn)

heapdiff.cc文件中來看heapdiff::HeapDiff::Initialize(target);的實現(xiàn)。

void heapdiff::HeapDiff::Initialize ( v8::Handle target )
{
    Nan::HandleScope scope;
    
    v8::Local t = Nan::New(New);
    t->InstanceTemplate()->SetInternalFieldCount(1);
    t->SetClassName(Nan::New("HeapDiff").ToLocalChecked());

    Nan::SetPrototypeMethod(t, "end", End);
    target->Set(Nan::New("HeapDiff").ToLocalChecked(), t->GetFunction());
}

Initialize函數(shù)中創(chuàng)建一個叫做HeapDiff的函數(shù)t,同時在t的原型鏈上綁了end方法,使得js層面可以執(zhí)行vat hp = new memwatch.HeapDiff();hp.end()

new memwatch.HeapDiff實現(xiàn)

當(dāng)js執(zhí)行new memwatch.HeapDiff();的時候,c++層面會執(zhí)行heapdiff::HeapDiff::New函數(shù),去掉注釋和不必要的宏,New函數(shù)精簡如下:

NAN_METHOD(heapdiff::HeapDiff::New)
{
    if (!info.IsConstructCall()) {
        return Nan::ThrowTypeError("Use the new operator to create instances of this object.");
    }

    Nan::HandleScope scope;

    HeapDiff * self = new HeapDiff();
    self->Wrap(info.This());

    s_inProgress = true;
    s_startTime = time(NULL);
    
    self->before = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL);

    s_inProgress = false;

    info.GetReturnValue().Set(info.This());
}

可以看到用戶在js層面執(zhí)行var hp = new memwatch.HeapDiff();的時候,c++層面會調(diào)用nodejs中的v8的api對對堆上內(nèi)存打一個snapshot保存到self->before中,并將當(dāng)前對象返回出去。

memwatch.HeapDiff.End實現(xiàn)

當(dāng)用戶執(zhí)行hp.end()的時候,會執(zhí)行原型鏈上的end方法,也就是c++的heapdiff::HeapDiff::End方法。同樣去掉冗余的注釋以及宏,End方法可以精簡如下:

NAN_METHOD(heapdiff::HeapDiff::End)
{
    Nan::HandleScope scope;

    HeapDiff *t = Unwrap( info.This() );

    if (t->ended) {
        return Nan::ThrowError("attempt to end() a HeapDiff that was already ended");
    }
    t->ended = true;

    s_inProgress = true;
    t->after = v8::Isolate::GetCurrent()->GetHeapProfiler()->TakeHeapSnapshot(NULL);
    s_inProgress = false;

    v8::Local comparison = compare(t->before, t->after);
    
    ((HeapSnapshot *) t->before)->Delete();
    t->before = NULL;
    ((HeapSnapshot *) t->after)->Delete();
    t->after = NULL;

    info.GetReturnValue().Set(comparison);
}

在End函數(shù)中,拿到當(dāng)前的HeapDiff對象之后,再對當(dāng)前的堆上內(nèi)存再打一個snapshot,調(diào)用compare函數(shù)對前后兩個snapshot對比后得到comparison后,將前后兩次snapshot對象釋放掉,并將結(jié)果通知給js。

下面分析下compare函數(shù)的具體實現(xiàn):
compare函數(shù)內(nèi)部會遞歸調(diào)用buildIDSet函數(shù)得到最終堆快照的diff結(jié)果。

static v8::Local
compare(const v8::HeapSnapshot * before, const v8::HeapSnapshot * after)
{
    Nan::EscapableHandleScope scope;
    int s, diffBytes;

    Local o = Nan::New();

    // first let"s append summary information
    Local b = Nan::New();
    b->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(before->GetNodesCount()));
    //b->Set(Nan::New("time"), s_startTime);
    o->Set(Nan::New("before").ToLocalChecked(), b);

    Local a = Nan::New();
    a->Set(Nan::New("nodes").ToLocalChecked(), Nan::New(after->GetNodesCount()));
    //a->Set(Nan::New("time"), time(NULL));
    o->Set(Nan::New("after").ToLocalChecked(), a);

    // now let"s get allocations by name
    set beforeIDs, afterIDs;
    s = 0;
    buildIDSet(&beforeIDs, before->GetRoot(), s);
    b->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s));
    b->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked());

    diffBytes = s;
    s = 0;
    buildIDSet(&afterIDs, after->GetRoot(), s);
    a->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(s));
    a->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(s).c_str()).ToLocalChecked());

    diffBytes = s - diffBytes;

    Local c = Nan::New();
    c->Set(Nan::New("size_bytes").ToLocalChecked(), Nan::New(diffBytes));
    c->Set(Nan::New("size").ToLocalChecked(), Nan::New(mw_util::niceSize(diffBytes).c_str()).ToLocalChecked());
    o->Set(Nan::New("change").ToLocalChecked(), c);

    // before - after will reveal nodes released (memory freed)
    vector changedIDs;
    setDiff(beforeIDs, afterIDs, changedIDs);
    c->Set(Nan::New("freed_nodes").ToLocalChecked(), Nan::New(changedIDs.size()));

    // here"s where we"ll collect all the summary information
    changeset changes;

    // for each of these nodes, let"s aggregate the change information
    for (unsigned long i = 0; i < changedIDs.size(); i++) {
        const HeapGraphNode * n = before->GetNodeById(changedIDs[i]);
        manageChange(changes, n, false);
    }

    changedIDs.clear();

    // after - before will reveal nodes added (memory allocated)
    setDiff(afterIDs, beforeIDs, changedIDs);

    c->Set(Nan::New("allocated_nodes").ToLocalChecked(), Nan::New(changedIDs.size()));

    for (unsigned long i = 0; i < changedIDs.size(); i++) {
        const HeapGraphNode * n = after->GetNodeById(changedIDs[i]);
        manageChange(changes, n, true);
    }

    c->Set(Nan::New("details").ToLocalChecked(), changesetToObject(changes));

    return scope.Escape(o);
}

該函數(shù)中構(gòu)造了兩個對象b(before)、a(after)用于保存前后兩個快照的詳細(xì)信息。用一個js對象描述如下:

// b(before) / a(after)
{
    nodes: // heap snapshot中對象節(jié)點個數(shù)
    size_bytes: // heap snapshot的對象大小(bytes)
    size: // heap snapshot的對象大小(kb、mb)
    
}

進(jìn)一步對前后兩次的快照進(jìn)行分析可以得到o,o中的before、after對象就是前后兩次的snapshot對象的引用:

// o 
{
    before: { // before的堆snapshot
        nodes:
        size_bytes:
        size: 
    },
    after: { // after的堆snapshot
        nodes:
        size_bytes:
        size: 
    },
    change: {
        freed_nodes: // gc掉的節(jié)點數(shù)量
        allocated_nodes: // 新增節(jié)點數(shù)量
        details: [ // 按照類型String、Array聚合出來的詳細(xì)信息
            {
                Array : {
                    what: // 類型
                    size_bytes: // 字節(jié)數(shù)bytes
                    size: // kb、mb
                    +: // 新增數(shù)量
                    -: // gc數(shù)量
                }
            },
            {}
        ]
    }
}

得到兩次snapshot對比的結(jié)果后將o返回出去,在End函數(shù)中通過info.GetReturnValue().Set(comparison);將結(jié)果傳遞到j(luò)s層面。

下面來具體說下compare函數(shù)中的buildIDSet、setDiff以及manageChange函數(shù)的實現(xiàn)。
buildIDSet的用法:buildIDSet(&beforeIDs, before->GetRoot(), s);,該函數(shù)會從堆snapshot的根節(jié)點出發(fā),遞歸的尋找所有能夠訪問的子節(jié)點,加入到集合seen中,做DFS統(tǒng)計所有可達(dá)節(jié)點的同時,也會對所有節(jié)點的shallowSize(對象本身占用的內(nèi)存,不包括引用的對象所占內(nèi)存)進(jìn)行累加,統(tǒng)計當(dāng)前堆所占用的內(nèi)存大小。其具體實現(xiàn)如下:

static void buildIDSet(set * seen, const HeapGraphNode* cur, int & s)
{
    Nan::HandleScope scope;
    if (seen->find(cur->GetId()) != seen->end()) {
        return;
    }
    if (cur->GetType() == HeapGraphNode::kObject &&
        handleToStr(cur->GetName()).compare("HeapDiff") == 0)
    {
        return;
    }
    s += cur->GetShallowSize();
    seen->insert(cur->GetId());
    for (int i=0; i < cur->GetChildrenCount(); i++) {
        buildIDSet(seen, cur->GetChild(i)->GetToNode(), s);
    }
}

setDiff函數(shù)用法:setDiff(beforeIDs, afterIDs, changedIDs);主要用來計算集合差集用的,具體實現(xiàn)很簡單,這里直接貼代碼,不再贅述:

typedef set idset;

// why doesn"t STL work?
// XXX: improve this algorithm
void setDiff(idset a, idset b, vector &c)
{
    for (idset::iterator i = a.begin(); i != a.end(); i++) {
        if (b.find(*i) == b.end()) c.push_back(*i);
    }
}

manageChange函數(shù)用法:manageChange(changes, n, false);,其作用在于做數(shù)據(jù)的聚合。對某個指定的set,按照set中對象的類型,聚合出每種對象創(chuàng)建了多少、銷毀了多少,實現(xiàn)如下:

static void manageChange(changeset & changes, const HeapGraphNode * node, bool added)
{
    std::string type;
    switch(node->GetType()) {
        case HeapGraphNode::kArray:
            type.append("Array");
            break;
        case HeapGraphNode::kString:
            type.append("String");
            break;
        case HeapGraphNode::kObject:
            type.append(handleToStr(node->GetName()));
            break;
        case HeapGraphNode::kCode:
            type.append("Code");
            break;
        case HeapGraphNode::kClosure:
            type.append("Closure");
            break;
        case HeapGraphNode::kRegExp:
            type.append("RegExp");
            break;
        case HeapGraphNode::kHeapNumber:
            type.append("Number");
            break;
        case HeapGraphNode::kNative:
            type.append("Native");
            break;
        case HeapGraphNode::kHidden:
        default:
            return;
    }

    if (changes.find(type) == changes.end()) {
        changes[type] = change();
    }

    changeset::iterator i = changes.find(type);
    i->second.size += node->GetShallowSize() * (added ? 1 : -1);
    if (added) i->second.added++;
    else i->second.released++;
    return;
}
upon_gcgc實現(xiàn)

這兩個方法的在init函數(shù)中聲明如下:

Nan::SetMethod(target, "upon_gc", memwatch::upon_gc);
Nan::SetMethod(target, "gc", memwatch::trigger_gc);

先看gc方法的實現(xiàn),實際上對應(yīng)memwatch::trigger_gc,實現(xiàn)如下:

NAN_METHOD(memwatch::trigger_gc) {
    Nan::HandleScope scope;
    int deadline_in_ms = 500;
    if (info.Length() >= 1 && info[0]->IsNumber()) {
        deadline_in_ms = (int)(info[0]->Int32Value()); 
    }
    Nan::IdleNotification(deadline_in_ms);
    Nan::LowMemoryNotification();
    info.GetReturnValue().Set(Nan::Undefined());
}

通過Nan::IdleNotificationNan::LowMemoryNotification觸發(fā)v8的gc功能。
再來看upon_gc方法,該方法實際上會綁定一個函數(shù),當(dāng)執(zhí)行到gc方法時,就會觸發(fā)該函數(shù):

NAN_METHOD(memwatch::upon_gc) {
    Nan::HandleScope scope;
    if (info.Length() >= 1 && info[0]->IsFunction()) {
        uponGCCallback = new UponGCCallback(info[0].As());
    }
    info.GetReturnValue().Set(Nan::Undefined());
}

其中info[0]就是用戶傳入的回調(diào)函數(shù)。調(diào)用new UponGCCallback的時候,其對應(yīng)的構(gòu)造函數(shù)內(nèi)部會執(zhí)行:

UponGCCallback(v8::Local callback_) : Nan::AsyncResource("memwatch:upon_gc") {
        callback.Reset(callback_);
}

把用戶傳入的callback_函數(shù)設(shè)置到UponGCCallback類的成員變量callback上。upon_gc回調(diào)的觸發(fā)與gc的鉤子有關(guān),詳細(xì)看下一節(jié)分析。

gc前、后鉤子函數(shù)的實現(xiàn)

gc鉤子的掛載如下:

Nan::AddGCPrologueCallback(memwatch::before_gc);
Nan::AddGCEpilogueCallback(memwatch::after_gc);

先來看memwatch::before_gc函數(shù)的實現(xiàn),內(nèi)部給gc開始記錄了時間:

NAN_GC_CALLBACK(memwatch::before_gc) {
    currentGCStartTime = uv_hrtime();
}

再來看memwatch::after_gc函數(shù)的實現(xiàn),內(nèi)部會在gc后記錄gc的結(jié)果到GCStats結(jié)構(gòu)體中:

struct GCStats {
    // counts of different types of gc events
    size_t gcScavengeCount; // gc 掃描次數(shù)
    uint64_t gcScavengeTime; // gc 掃描事件

    size_t gcMarkSweepCompactCount; //  gc標(biāo)記清除整理的個數(shù)
    uint64_t gcMarkSweepCompactTime; // gc標(biāo)記清除整理的時間

    size_t gcIncrementalMarkingCount;  // gc增量標(biāo)記的個數(shù)
    uint64_t gcIncrementalMarkingTime; // gc增量標(biāo)記的時間

    size_t gcProcessWeakCallbacksCount; // gc處理weakcallback的個數(shù)
    uint64_t gcProcessWeakCallbacksTime; // gc處理weakcallback的時間
};

對gc請求進(jìn)行統(tǒng)計后,通過v8的api獲取堆的使用情況,最終將結(jié)果保存到barton中,barton內(nèi)部維護(hù)了一個uv_work_t的變量req,req的data字段指向barton對象本身。

NAN_GC_CALLBACK(memwatch::after_gc) {
    if (heapdiff::HeapDiff::InProgress()) return;
    uint64_t gcEnd = uv_hrtime();
    uint64_t gcTime = gcEnd - currentGCStartTime;
    switch(type) {
        case kGCTypeScavenge:
            s_stats.gcScavengeCount++;
            s_stats.gcScavengeTime += gcTime;
            return;
        case kGCTypeMarkSweepCompact:
        case kGCTypeAll:
            break;
    }

    if (type == kGCTypeMarkSweepCompact) {
        s_stats.gcMarkSweepCompactCount++;
        s_stats.gcMarkSweepCompactTime += gcTime;

        Nan::HandleScope scope;

        Baton * baton = new Baton;
        v8::HeapStatistics hs;

        Nan::GetHeapStatistics(&hs);

        timeval tv;
        gettimeofday(&tv, NULL);

        baton->gc_ts = (tv.tv_sec * 1000000) + tv.tv_usec;

        baton->total_heap_size = hs.total_heap_size();
        baton->total_heap_size_executable = hs.total_heap_size_executable();
        baton->req.data = (void *) baton;

        uv_queue_work(uv_default_loop(), &(baton->req),
            noop_work_func, (uv_after_work_cb)AsyncMemwatchAfter);
    }
}

在前面工作完成的基礎(chǔ)上,將結(jié)果丟到libuv的loop中,等到合適的實際觸發(fā)回調(diào)函數(shù),在回調(diào)函數(shù)中可以拿到req對象,通過訪問req.data對其做強(qiáng)制類型裝換可以得到barton對象,在loop的回調(diào)函數(shù)中,將barton中封裝的數(shù)據(jù)依次取出來,保存到stats對象中,并調(diào)用uponGCCallback的Call方法,傳入字面量stats和stats對象。

static void AsyncMemwatchAfter(uv_work_t* request) {
    Nan::HandleScope scope;

    Baton * b = (Baton *) request->data;
    // if there are any listeners, it"s time to emit!
    if (uponGCCallback) {
        Local argv[2];

        Local stats = Nan::New();

        stats->Set(Nan::New("gc_ts").ToLocalChecked(), javascriptNumber(b->gc_ts));

        stats->Set(Nan::New("gcProcessWeakCallbacksCount").ToLocalChecked(), javascriptNumberSize(b->stats.gcProcessWeakCallbacksCount));
        stats->Set(Nan::New("gcProcessWeakCallbacksTime").ToLocalChecked(), javascriptNumber(b->stats.gcProcessWeakCallbacksTime));

        stats->Set(Nan::New("peak_malloced_memory").ToLocalChecked(), javascriptNumberSize(b->peak_malloced_memory));
        stats->Set(Nan::New("gc_time").ToLocalChecked(), javascriptNumber(b->gc_time));

        // the type of event to emit
        argv[0] = Nan::New("stats").ToLocalChecked();
        argv[1] = stats;
        uponGCCallback->Call(2, argv);
    }

    delete b;
}

最后在Call函數(shù)的內(nèi)部調(diào)用js傳入的callback_函數(shù),并將字面量stats和stats對象傳遞到j(luò)s層面,供上層用戶使用。

void Call(int argc, Local argv[]) {
        v8::Isolate *isolate = v8::Isolate::GetCurrent();
        runInAsyncScope(isolate->GetCurrentContext()->Global(), Nan::New(callback), argc, argv);
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/100828.html

相關(guān)文章

  • 用NAN寫一個nodejs的c++擴(kuò)展

    摘要:介紹的全稱為其表現(xiàn)上是一個包。引用擴(kuò)展,暴露出方法供上層使用。初次開發(fā)擴(kuò)展的用戶需要注意下項目目錄中的文件會讀取項目中的為表示最后生成的擴(kuò)展文件名為。累加完成后通過將結(jié)果返回出去。 NAN介紹 NAN的全稱為Native Abstraction for Node.js, 其表現(xiàn)上是一個Node.js包。安裝后,就得到一堆C++頭文件,里面是一堆宏。它主要為Node.js和V8跨版本提供...

    魏明 評論0 收藏0
  • 2017-06-23 前端日報

    摘要:前端日報精選大前端公共知識梳理這些知識你都掌握了嗎以及在項目中的實踐深入貫徹閉包思想,全面理解閉包形成過程重溫核心概念和基本用法前端學(xué)習(xí)筆記自定義元素教程阮一峰的網(wǎng)絡(luò)日志中文譯回調(diào)是什么鬼掘金譯年,一個開發(fā)者的好習(xí)慣知乎專 2017-06-23 前端日報 精選 大前端公共知識梳理:這些知識你都掌握了嗎?Immutable.js 以及在 react+redux 項目中的實踐深入貫徹閉包思...

    Vixb 評論0 收藏0
  • [ 好文分享 ] 美團(tuán)酒店Node全棧開發(fā)實踐

    摘要:我所在的美團(tuán)酒店事業(yè)部去年月份成立,新的業(yè)務(wù)新的開發(fā)團(tuán)隊,這一切使得我們的前后端分離推進(jìn)的很徹底。日志監(jiān)控平臺日志監(jiān)控平臺是美團(tuán)內(nèi)部的一個日志收集系統(tǒng),目前美團(tuán)統(tǒng)一使用收集日志,具有接收格式日志的能力,而日志監(jiān)控平臺也是以格式日志來收集。 轉(zhuǎn)自:美團(tuán)技術(shù)團(tuán)隊 作者:美團(tuán)技術(shù)團(tuán)隊 分享理由:很好的分享,可見,基于Node的前后端分離的架構(gòu)是越顯流行和重要,前端攻城獅們,No...

    wangdai 評論0 收藏0
  • 前端性能優(yōu)化指南

    摘要:前端性能優(yōu)化指南優(yōu)化緩存異步并不等于即時。操作性能問題主要有以下原因。發(fā)生在之前,所以相對來說會造成更多性能損耗。新引擎還對對象屬性訪問做了優(yōu)化,解決方案叫,簡稱。代價是前置的掃描類型編譯優(yōu)化。數(shù)組,,閉包變量不在優(yōu)化范疇之列。 前端性能優(yōu)化指南 AJAX優(yōu)化 緩存AJAX: 異步并不等于即時。 請求使用GET: 當(dāng)使用XMLHttpRequest時,而URL長度不到2K...

    Pink 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<