Hone logo
Hone
Problems

Implementing a Mark and Sweep Garbage Collector in Go

Garbage collection is a fundamental concept in memory management, automatically reclaiming memory that is no longer in use by a program. The Mark and Sweep algorithm is a classic and widely understood garbage collection technique. This challenge involves implementing a simplified Mark and Sweep garbage collector in Go to manage a small, custom heap.

Problem Description

Your task is to implement a basic Mark and Sweep garbage collector in Go. You will simulate a small heap and objects residing within it. The garbage collector should identify and reclaim memory occupied by objects that are no longer reachable from a set of "root" objects.

What Needs to Be Achieved:

  1. Heap Simulation: Create a data structure to represent a simple memory heap. This heap will store objects.
  2. Object Representation: Define a way to represent objects in the heap. Each object should have a unique ID, some data (e.g., an integer value), and a list of references to other objects in the heap.
  3. Root Set: Maintain a set of "root" objects. These are objects that are considered directly accessible by the program.
  4. Mark Phase: Implement the "mark" phase of the algorithm. Starting from the root set, traverse all reachable objects and mark them as "live."
  5. Sweep Phase: Implement the "sweep" phase. Iterate through the entire heap. Any object that was not marked during the mark phase is considered garbage and should be reclaimed (removed from the heap).

Key Requirements:

  • The heap should be able to store a collection of simulated objects.
  • Objects must be able to reference other objects.
  • The garbage collector should be explicitly triggered (e.g., via a Collect() method).
  • The Collect() method should correctly identify and remove unreachable objects.
  • You should be able to inspect the heap to verify which objects are still present.

Expected Behavior:

When Collect() is called, the garbage collector should:

  1. Unmark all previously marked objects.
  2. Start from the root set and traverse the object graph.
  3. Mark every reachable object.
  4. Remove any object from the heap that remains unmarked.

Edge Cases to Consider:

  • Empty Heap: What happens if the heap is empty?
  • No Roots: What if there are no root objects? All objects should be considered garbage.
  • Cyclic References: The algorithm should correctly handle cycles of references (e.g., object A references B, and B references A).
  • Object with No References: An object that is not referenced by any other object and is not a root should be collected.

Examples

Example 1:

Initial Heap:
[Obj1 (ID: 1, Value: 10, Refs: [2])]
[Obj2 (ID: 2, Value: 20, Refs: [])]
[Obj3 (ID: 3, Value: 30, Refs: [])]

Root Set: [Obj1]

After calling Collect():
Output Heap:
[Obj1 (ID: 1, Value: 10, Refs: [2])]
[Obj2 (ID: 2, Value: 20, Refs: [])]

Explanation: Obj1 is a root. It references Obj2. Obj3 is not referenced by any root or reachable object, so it is collected.

Example 2:

Initial Heap:
[ObjA (ID: 10, Value: 100, Refs: [20])]
[ObjB (ID: 20, Value: 200, Refs: [10])] // Cyclic reference
[ObjC (ID: 30, Value: 300, Refs: [])]

Root Set: [ObjA]

After calling Collect():
Output Heap:
[ObjA (ID: 10, Value: 100, Refs: [20])]
[ObjB (ID: 20, Value: 200, Refs: [10])]

Explanation: ObjA is a root and references ObjB. ObjB references ObjA. This cycle makes both ObjA and ObjB reachable. ObjC is not reachable and is collected.

Example 3: Empty Heap

Initial Heap: []
Root Set: []

After calling Collect():
Output Heap: []

Explanation: An empty heap remains empty.

Example 4: No Roots

Initial Heap:
[ObjX (ID: 100, Value: 1000, Refs: [])]
[ObjY (ID: 101, Value: 1001, Refs: [])]

Root Set: []

After calling Collect():
Output Heap: []

Explanation: With no roots, no objects are considered reachable, and all are collected.

Constraints

  • The heap size is limited to at most 1000 objects.
  • Each object can reference at most 10 other objects.
  • Object IDs will be unique positive integers.
  • The garbage collector should complete its operation within a reasonable time for the given constraints (e.g., a few milliseconds for a heap of 1000 objects).

Notes

  • You'll need to design data structures for your Heap, Object, and the GarbageCollector itself.
  • Consider how you will represent the "marked" state of an object. A boolean flag within the object struct is a common approach.
  • The traversal for the mark phase can be implemented recursively or iteratively using a stack.
  • Think about how you'll manage object lookup by ID within the heap. A map is often suitable.
  • You do not need to implement actual memory allocation or deallocation; this is a simulation. The "reclaiming" of memory means removing the object from your heap's collection.
  • Focus on the logic of the Mark and Sweep algorithm itself.
Loading editor...
go