【好代码分享】函数指针结构体用法

QuickJS 是大神 (Fabrice Bellard) 写的一个 JavaScript 引擎
优势:
1. 纯 C,没外部库依赖
2. 代码体积小,Hello world 在 x86 上只占 200 多 K
3. 通过 ECMAScript 标准测试,支持 ES2019 绝大部份
4. Benchmark 性能也非常不错,具体看官网报告

其它详见: https://bellard.org/quickjs

今天给大家分享一个他的内存分配接口设计

struct JSRuntime {
    JSMallocFunctions mf;
    JSMallocState malloc_state;
    const char *rt_info;

    ...

    struct list_head gc_obj_list;

    ...

};
这是虚拟机运行结构体

创建函数看一下

JSRuntime *JS_NewRuntime(void)
{
    return JS_NewRuntime2(&def_malloc_funcs, NULL);
}

JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
{
    ...
}

我们关注 JSMallocFunctions 及 JSMallocState 二个结构体, def_malloc_funcs 的参数在下面实现。

typedef struct JSMallocState {
    size_t malloc_count;
    size_t malloc_size;
    size_t malloc_limit;
    void *opaque; /* user opaque */
} JSMallocState;

typedef struct JSMallocFunctions {
    void *(*js_malloc)(JSMallocState *s, size_t size);
    void (*js_free)(JSMallocState *s, void *ptr);
    void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
    size_t (*js_malloc_usable_size)(const void *ptr);
} JSMallocFunctions;

这里的一个小技巧是,用函数指针组成的结构体,来实现 C++ 的类接口,来用实现内存分配及销毁的钩子,实现内存分配器可外接

static const JSMallocFunctions def_malloc_funcs = {
    js_def_malloc,
    js_def_free,
    js_def_realloc,
#if defined(__APPLE__)
    malloc_size,
#elif defined(_WIN32)
    (size_t (*)(const void *))_msize,
#elif defined(EMSCRIPTEN)
    NULL,
#elif defined(__linux__)
    (size_t (*)(const void *))malloc_usable_size,
#else
    /* change this to `NULL,` if compilation fails */
    malloc_usable_size,
#endif
};

这里用一个静态结构给来定义默认实现,我们可以关注一下 js_def_malloc 的玩法,看看是如何默认实现的,看代码

static void *js_def_malloc(JSMallocState *s, size_t size)
{
    void *ptr;

    /* Do not allocate zero bytes: behavior is platform dependent */
    assert(size != 0);

    if (unlikely(s->malloc_size + size > s->malloc_limit))
        return NULL;

    ptr = malloc(size);
    if (!ptr)
        return NULL;

    s->malloc_count++;
    s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
    return ptr;
}

可以看出,在内存分配的时候,用到了 JSMallocState 进行记录,这样可以很容易的实现最初级的内存泄露检查,那么问题来了,可是如果一但 malloc_count 真不归 0,如何排查是哪块有泄露呢?

这块作者没有在 JSMallocState 做更详细的记录,因为引擎除了工作需要申请的对像,大量都是 Script 中引来的对像,这个对像有一个 GC 管理器,是在 Runtime 里有一个 gc_obj_list,链表进行管理的,在宏 DUMP_LEAKS 生效后,能够检测出来。

虽然没有太多的技术含量,也要总结一下:
1. 内存管理设计的时候一定可让外界接管,包括 SGI 里也是一样的,方便不同环境用不同的内存分配器优化。
2. 通过函数结构体,可以很容易的实现面向接口变程,方便对接各种系统
3. 对外扩展是灵活的,内部是相对封闭的,符合开闭原则。

功能越是简单,越是基础,越不能偷懒

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值