Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions jvm/src/external_roots.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use alloc::vec::Vec;

use crate::ClassInstance;

/// Discovers roots in a consumer's execution state during synchronous collection.
/// Implementations must only inspect existing state, without calling JVM APIs or mutating the object inventory.
pub trait ExternalRoots: Send + Sync {
fn roots(&self, objects: &[&dyn ClassInstance]) -> anyhow::Result<Vec<usize>>;
}
73 changes: 41 additions & 32 deletions jvm/src/garbage_collector.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
use jvm_types::FieldAccessFlags;

use hashbrown::{HashSet, hash_set::Entry};
use hashbrown::HashSet;

use crate::{ClassDefinition, ClassInstance, Field, JavaValue, Jvm, class_loader::Class, thread::JvmThread};

Expand All @@ -12,37 +12,46 @@ pub fn determine_garbage(
all_class_instances: &HashSet<Box<dyn ClassInstance>>,
classes: &BTreeMap<String, Class>,
interned_strings: &[Box<dyn ClassInstance>],
) -> Vec<Box<dyn ClassInstance>> {
root_identities: &HashSet<usize>,
) -> anyhow::Result<Vec<Box<dyn ClassInstance>>> {
let mut reachable_objects = HashSet::new();

classes.values().for_each(|x| {
find_reachable_objects(jvm, &x.java_class(), &mut reachable_objects);
find_static_reachable_objects(jvm, x, &mut reachable_objects);
});
for class in classes.values() {
find_reachable_objects(jvm, &class.java_class(), &mut reachable_objects)?;
find_static_reachable_objects(jvm, class, &mut reachable_objects)?;
}

threads
for object in threads
.values()
.flat_map(|thread| thread.iter_frame().flat_map(|stack| stack.local_variables()))
.for_each(|x| {
find_reachable_objects(jvm, x, &mut reachable_objects);
});
{
find_reachable_objects(jvm, object, &mut reachable_objects)?;
}

threads.values().filter_map(|thread| thread.java_thread()).for_each(|x| {
find_reachable_objects(jvm, x, &mut reachable_objects);
});
for object in threads.values().filter_map(|thread| thread.java_thread()) {
find_reachable_objects(jvm, object, &mut reachable_objects)?;
}

global_references.values().for_each(|object| {
find_reachable_objects(jvm, object, &mut reachable_objects);
});
for object in global_references.values() {
find_reachable_objects(jvm, object, &mut reachable_objects)?;
}

interned_strings.iter().for_each(|x| {
find_reachable_objects(jvm, x, &mut reachable_objects);
});
for object in interned_strings {
find_reachable_objects(jvm, object, &mut reachable_objects)?;
}

for object in all_class_instances.iter().filter(|object| root_identities.contains(&object.identity())) {
find_reachable_objects(jvm, object, &mut reachable_objects)?;
}

all_class_instances.difference(&reachable_objects).cloned().collect()
Ok(all_class_instances
.iter()
.filter(|object| !reachable_objects.contains(&object.identity()))
.cloned()
.collect())
}

fn find_static_reachable_objects(jvm: &Jvm, class: &Class, reachable_objects: &mut HashSet<Box<dyn ClassInstance>>) {
fn find_static_reachable_objects(jvm: &Jvm, class: &Class, reachable_objects: &mut HashSet<usize>) -> anyhow::Result<()> {
let fields = find_all_fields(jvm, &*class.definition);
for field in fields {
if !field.access_flags().contains(FieldAccessFlags::STATIC) {
Expand All @@ -52,33 +61,32 @@ fn find_static_reachable_objects(jvm: &Jvm, class: &Class, reachable_objects: &m
let descriptor = field.descriptor();

if (descriptor.starts_with('L') && descriptor.ends_with(';')) || descriptor.starts_with('[') {
let value = class.definition.get_static_field(&*field).unwrap();
let value = class.definition.get_static_field(&*field)?;
if let JavaValue::Object(Some(value)) = value {
find_reachable_objects(jvm, &value, reachable_objects);
find_reachable_objects(jvm, &value, reachable_objects)?;
}
}
}
Ok(())
}

#[allow(clippy::borrowed_box)]
fn find_reachable_objects(jvm: &Jvm, object: &Box<dyn ClassInstance>, reachable_objects: &mut HashSet<Box<dyn ClassInstance>>) {
let entry = reachable_objects.entry(object.clone());
if let Entry::Occupied(_) = entry {
return;
fn find_reachable_objects(jvm: &Jvm, object: &Box<dyn ClassInstance>, reachable_objects: &mut HashSet<usize>) -> anyhow::Result<()> {
if !reachable_objects.insert(object.identity()) {
return Ok(());
}
entry.insert();

let class_definition = object.class_definition();
let name = class_definition.name();
if name.starts_with('[') {
if name.starts_with("[L") || name.starts_with("[[") {
// is object array
let array = object.as_array_instance().unwrap();
let values = array.load(0, array.length()).unwrap();
let values = array.load(0, array.length())?;

for value in values {
if let JavaValue::Object(Some(value)) = value {
find_reachable_objects(jvm, &value, reachable_objects);
find_reachable_objects(jvm, &value, reachable_objects)?;
}
}
}
Expand All @@ -93,13 +101,14 @@ fn find_reachable_objects(jvm: &Jvm, object: &Box<dyn ClassInstance>, reachable_
let descriptor = field.descriptor();

if (descriptor.starts_with('L') && descriptor.ends_with(';')) || descriptor.starts_with('[') {
let value = object.get_field(&*field).unwrap();
let value = object.get_field(&*field)?;
if let JavaValue::Object(Some(value)) = value {
find_reachable_objects(jvm, &value, reachable_objects);
find_reachable_objects(jvm, &value, reachable_objects)?;
}
}
}
}
Ok(())
}

fn find_all_fields(jvm: &Jvm, class_definition: &dyn ClassDefinition) -> Vec<Box<dyn Field>> {
Expand Down
Loading
Loading