一个简单动态语言虚拟机实现

最近在看Lua的源码,但Lua的代码太庞大了(虽然相较于其他语言非常精巧),所以想自己写一个小的带垃圾回收的虚拟机,加深自己对垃圾回收原理的理解和记忆,以下是代码:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define STACK_MAX 256 // 最大的栈数量
#define INIT_GC_VAL_MAX_NUM 8 // 初始最大的GC Value数量,达到后触发第一次GC

// 虚拟机中一共有两种类型,整形和元组
typedef enum {
	VALUE_TYPE_INT,
	VALUE_TYPE_PAIR
} ValueType;

// 值定义
typedef struct _Value {
	ValueType type; // 值类型
	uint8_t marked; // GC 标记

	struct _Value* next; // GC 对象链

	// 值集合
	union {
		int value;
		struct { // 有两个元素的元组
			struct _Value* v1;
			struct _Value* v2;
		};
	};
} Value;

// 虚拟机定义,模仿Lua叫State
typedef struct {
	Value* stack[STACK_MAX]; // 栈
	int stackSize; // 当前栈大小
	Value* gcFirstVal; // 指向对象链表的首个节点
	int gcValNum; // 当前对象数量
	int gcMaxValNum; // 最大对象数量,达到后触发GC
} State;

void gc(State* s);

// 创建一个新的虚拟机
State* newState() {
	State* s = malloc(sizeof(State));
	s->stackSize = 0;

	s->gcFirstVal = NULL;
	s->gcValNum = 0;
	s->gcMaxValNum = INIT_GC_VAL_MAX_NUM;
	return s;
}

// 向栈中压入数据
void push(State* s, Value* v) {
	s->stack[s->stackSize++] = v;
}

// 从栈中弹出数据
Value* pop(State* s) {
	return s->stack[--s->stackSize];
}

// 创建一个新的值
Value* newValue(State* s, ValueType type) {
	if (s->gcValNum == s->gcMaxValNum) {
		gc(s);
	}

	Value* v = malloc(sizeof(Value));
	v->type = type;
	v->next = s->gcFirstVal;
	s->gcFirstVal = v;
	v->marked = 0;

	s->gcValNum++;
	return v;
}

// 向栈中压入一个整形数值
void pushInt(State* s, int value) {
	Value* v = newValue(s, VALUE_TYPE_INT);
	v->value = value;
	push(s, v);
}

// 依据栈顶的两个值创建一个元组
Value* pushPair(State* s) {
	Value* v = newValue(s, VALUE_TYPE_PAIR);
	v->v1 = pop(s);
	v->v2 = pop(s);
	push(s, v);
	return v;
}

// 以下是垃圾回收实现

// 标记单个对象
void mark(Value* v) {
	if (v->marked) return;

	v->marked = 1;

	if (v->type == VALUE_TYPE_PAIR) {
		mark(v->v1);
		mark(v->v2);
	}
}

// 标记所有对象
void markAll(State* s) {
	for (int i = 0; i < s->stackSize; i++) {
		mark(s->stack[i]);
	}
}

// 清除未标记的对象
void sweep(State* s) {
	Value** v = &(s->gcFirstVal);
	while (*v) {
		if (!(*v)->marked) {
			Value* unreached = *v;
			*v = unreached->next;
			free(unreached);

			s->gcValNum--;
		} else {
			(*v)->marked = 0;
			v = &(*v)->next;
		}
	}
}

// 垃圾回收
void gc(State* s) {
	int valNum = s->gcValNum;
	markAll(s);
	sweep(s);

	s->gcMaxValNum = s->gcValNum == 0 ? INIT_GC_VAL_MAX_NUM : s->gcValNum * 2;
	printf("Collected Values count: %d, current Value count: %d\n", valNum - s->gcValNum, s->gcValNum);
}

// 释放虚拟机
void freeState(State* s) {
	s->stackSize = 0;
	gc(s);
	free(s);
}

void main() {
	State* s = newState(); // 
	pushInt(s, 1); // 1
	pushInt(s, 2); // 1, 2
	pop(s); // 1
	pop(s); // 
	pushInt(s, 3); // 3
	pushInt(s, 4); // 4
	pushPair(s); // (3, 4)

	pushInt(s, 1); // (3, 4), 1
	pushInt(s, 2); // (3, 4), 1, 2
	Value* v1 = pushPair(s); // // (3, 4), (1, 2)
	pushInt(s, 3); // 这里会触发GC // (3, 4), (1, 2), 3
	pushInt(s, 4); // (3, 4), (1, 2), 3, 4
	Value* v2 = pushPair(s); // (3, 4), (1, 2), (3, 4)
	v1->v2 = v2;
	v2->v1 = v1;

	pop(s); // (3, 4), (1, 2) // 这样写可能不太准确
	pop(s); // (3, 4)

	freeState(s);
}

运行后输出:

Collected Values count: 2, current Value count: 6
Collected Values count: 9, current Value count: 0

这只是个人用来学习的,所以没有加什么防御编程之类的东西,仅仅是展示标记清除的核心算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值