Experimenting with garbage collection algorithm.
This commit is contained in:
parent
34fae6ccca
commit
d09d945323
205
src/vm/mem/mod.rs
Normal file
205
src/vm/mem/mod.rs
Normal file
@ -0,0 +1,205 @@
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Mul;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Gc<T> {
|
||||
inner: *mut GcInner,
|
||||
data: *const T,
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GcInner {
|
||||
color: GcColor,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl GcInner {
|
||||
pub fn new(size: usize) -> Self {
|
||||
Self {
|
||||
color: GcColor::White,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum GcColor {
|
||||
White,
|
||||
Black,
|
||||
}
|
||||
|
||||
impl<T> Gc<T> {
|
||||
pub fn new(data: T) -> Self {
|
||||
let inner = Box::into_raw(Box::new(GcInner::new(size_of::<T>())));
|
||||
let boxed_data = Box::into_raw(Box::new(data)) as *const T;
|
||||
Self {
|
||||
inner,
|
||||
data: boxed_data,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_color(&mut self, color: GcColor) {
|
||||
unsafe {
|
||||
(*self.inner).color = color;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(&self) -> GcColor {
|
||||
unsafe { (*self.inner).color }
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
unsafe { (*self.inner).size }
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &T {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
|
||||
pub fn dealloc(&mut self) {
|
||||
unsafe {
|
||||
drop(Box::from_raw(self.inner));
|
||||
drop(Box::from_raw(self.data as *mut T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Gc<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner,
|
||||
data: self.data,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DvmValue {
|
||||
Primitive(usize),
|
||||
Object(Gc<RefCell<DvmObject>>),
|
||||
Nil,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DvmObject {
|
||||
fields: Vec<DvmValue>,
|
||||
}
|
||||
|
||||
impl DvmObject {
|
||||
pub fn new() -> Self {
|
||||
Self { fields: vec![] }
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &[DvmValue] {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
pub fn fields_mut(&mut self) -> &mut Vec<DvmValue> {
|
||||
&mut self.fields
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_garbage(
|
||||
next_gc: &mut usize,
|
||||
current_size: &mut usize,
|
||||
stack: &Vec<DvmValue>,
|
||||
gcs: &mut Vec<Gc<RefCell<DvmObject>>>,
|
||||
) {
|
||||
let mut gray_stack: Vec<Gc<RefCell<DvmObject>>> = vec![];
|
||||
|
||||
for stack_value in stack {
|
||||
match stack_value {
|
||||
DvmValue::Object(gc) => {
|
||||
if gc.color() == GcColor::White {
|
||||
gray_stack.push(gc.clone());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(mut current) = gray_stack.pop() {
|
||||
for field in current.data().borrow().fields() {
|
||||
match field {
|
||||
DvmValue::Object(object) => {
|
||||
gray_stack.push(object.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
current.set_color(GcColor::Black);
|
||||
}
|
||||
|
||||
let mut collected_size: usize = 0;
|
||||
|
||||
for i in (0..gcs.len()).rev() {
|
||||
if gcs[i].color() == GcColor::White {
|
||||
let mut gc = gcs.remove(i);
|
||||
let size = gc.size();
|
||||
*current_size -= size;
|
||||
collected_size += size;
|
||||
gc.dealloc();
|
||||
} else {
|
||||
gcs[i].set_color(GcColor::White);
|
||||
}
|
||||
}
|
||||
|
||||
*next_gc = std::cmp::max(1024 * 1024, current_size.mul(2));
|
||||
println!(
|
||||
"collected_size: {}, current_size: {}, next_gc: {}",
|
||||
collected_size, current_size, next_gc
|
||||
);
|
||||
}
|
||||
|
||||
fn maybe_collect_garbage(
|
||||
next_gc: &mut usize,
|
||||
current_size: &mut usize,
|
||||
stack: &Vec<DvmValue>,
|
||||
gcs: &mut Vec<Gc<RefCell<DvmObject>>>,
|
||||
) {
|
||||
if current_size > next_gc {
|
||||
collect_garbage(next_gc, current_size, stack, gcs);
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_dvm_object(
|
||||
next_gc: &mut usize,
|
||||
current_size: &mut usize,
|
||||
stack: &Vec<DvmValue>,
|
||||
gcs: &mut Vec<Gc<RefCell<DvmObject>>>,
|
||||
) -> Gc<RefCell<DvmObject>> {
|
||||
maybe_collect_garbage(next_gc, current_size, stack, gcs);
|
||||
let mut object = DvmObject::new();
|
||||
object.fields_mut().push(DvmValue::Nil);
|
||||
let gc = Gc::new(RefCell::new(object));
|
||||
*current_size += gc.size();
|
||||
gcs.push(gc.clone());
|
||||
gc
|
||||
}
|
||||
|
||||
fn set_field(obj: &mut Gc<RefCell<DvmObject>>, field_index: usize, value: DvmValue) {
|
||||
let mut binding = obj.data().borrow_mut();
|
||||
binding.fields_mut()[field_index] = value;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::vm::mem::{alloc_dvm_object, collect_garbage, DvmObject, DvmValue, Gc};
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut stack: Vec<DvmValue> = vec![];
|
||||
let mut heap: Vec<Gc<RefCell<DvmObject>>> = vec![];
|
||||
let mut next_gc: usize = 1024 * 1024;
|
||||
let mut current_size: usize = 0;
|
||||
for i in 0..100_000_000 {
|
||||
let mut o = alloc_dvm_object(&mut next_gc, &mut current_size, &stack, &mut heap);
|
||||
}
|
||||
collect_garbage(&mut next_gc, &mut current_size, &stack, &mut heap);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,7 @@ pub mod implementation;
|
||||
pub mod instruction;
|
||||
pub mod interface;
|
||||
pub mod lib;
|
||||
mod mem;
|
||||
pub mod method;
|
||||
pub mod object;
|
||||
pub mod op_codes;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user