在看 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
一样
嗯,既然 golang 已经做了这部工作,那我就需要担心内存不够如何处理的问题了(虽然很大概率遇不到)