【小记】golang 语言本身对于内存不够的处理

在看 redis 1.0 源码时,总会看到需要申请内存的地方,如果申请不到需要大的内存就会返回 NULL,然后在调用层抛出 oom。
比如 listDup 中在复制特殊 value 或者加入新节点时都有可能返回 NULL

if (copy->dup) {
    value = copy->dup(node->value);
    if (value == NUL) {
        ...
        return NULL;
    }
}
...
if (listAddNodeTail(copy, value) == NULL) {
    ...
    return NULL;
}

调用层中发现复制失败,抛出 oom

c->reply = listDup(slave->reply);
if (!c->reply) oom("listDup copying slave reply list");

oom 中执行 abort()

static void oom(const char *msg) {
    fprintf(stderr, "%s: Out of memory\n",msg);
    fflush(stderr);
    sleep(1);
    abort();
}

然后我就想到了 golang 的 make/new,平时都不会遇到 oom 的情况,感觉每次 make/new 都会成功,并且这两个函数都没有特殊返回值来标识失败的情况,因此在底层应该是做了处理。
由于之前在 mapassign 中看到分配桶的情节,最终在 mallocgc 函数中找到了目标函数 allocLarge

// allocLarge allocates a span for a large object.
func (c *mcache) allocLarge(size uintptr, noscan bool) *mspan {
    if size+_PageSize < size {
        throw("out of memory")
    }
    npages := size >> _pageShift
    if size&_PageMask != 0 {
        npages++
    }
    // Deduct credit for this span allocation and sweep if
    // necessary. mHeap_Alloc will also sweep npages, so this only
    // pays the debt down to npage pages.
    deductSweepCredit(npages*_PageSize, npages)
    spc := makeSpanClass(0, noscan)
    s := mheap_.alloc(npages, spc)
    if s == nil {
    	throw("out of memory")
    }
    ...
}

throw 最终调用 fatalthrow

// fatalthrow implements an unrecoverable runtime throw. It freezes the
// system, prints stack traces starting from its caller, and terminates the
// process.

最终也是终止程序,和 abort 一样

posted @ 2023-02-03 00:16  HelloEricy  阅读(60)  评论(0编辑  收藏  举报