Implementing Incremental Garbage Collection Simulation in Go
Simulating incremental garbage collection (GC) is a valuable exercise for understanding how modern GC systems work. This challenge asks you to build a simplified simulation of an incremental GC in Go, focusing on marking and sweeping phases. The goal is to demonstrate the core concepts of incremental collection without the complexities of a full-fledged GC implementation.
Problem Description
You are tasked with creating a Go program that simulates a basic incremental garbage collector. The program should manage a set of objects, track their references, and periodically perform a marking and sweeping phase. The simulation should allow for the creation of objects, the establishment of references between them, and the triggering of a GC cycle. The GC cycle should involve a marking phase (identifying reachable objects) and a sweeping phase (reclaiming memory occupied by unreachable objects). The simulation should provide a mechanism to observe the state of the heap before and after the GC cycle.
Key Requirements:
- Object Representation: Define a struct
Objectwith a unique ID and a slice of IDs representing references to other objects. - Heap Management: Implement a data structure (e.g., a slice or map) to represent the heap, storing the
Objectinstances. - Object Creation: Provide a function to create new objects and add them to the heap.
- Reference Establishment: Provide a function to establish references between objects in the heap.
- Marking Phase: Implement a marking phase that starts from a set of root objects (e.g., global variables) and recursively traverses the reference graph to mark all reachable objects. Use a simple marking strategy (e.g., a boolean flag on each object).
- Sweeping Phase: Implement a sweeping phase that iterates through the heap and reclaims memory occupied by unmarked objects. This can be simulated by removing the object from the heap.
- GC Triggering: Provide a function to trigger the GC cycle (marking and sweeping).
- Heap Observation: Provide a function to print the current state of the heap (object IDs and references) before and after the GC cycle.
Expected Behavior:
The program should:
- Allow the user to create objects and establish references between them.
- When the GC is triggered, it should correctly identify and reclaim unreachable objects.
- The heap observation function should accurately reflect the state of the heap before and after the GC.
Edge Cases to Consider:
- Circular References: The marking phase should correctly handle circular references (e.g., object A references object B, and object B references object A).
- Root Objects: The marking phase should start from a defined set of root objects.
- Empty Heap: The GC should handle the case where the heap is empty.
- No Unreachable Objects: The GC should handle the case where there are no unreachable objects.
Examples
Example 1:
Input:
- Create objects: 1, 2, 3, 4
- Establish references: 1 -> 2, 2 -> 3, 3 -> 1, 4 -> 1
- Trigger GC
Output:
- Heap before GC: [1: [2], 2: [3], 3: [1], 4: [1]]
- Heap after GC: [1: [2], 2: [3], 3: [1], 4: [1]] (No objects are unreachable)
Explanation: All objects are reachable from root object 4.
Example 2:
Input:
- Create objects: 1, 2, 3, 4
- Establish references: 1 -> 2, 2 -> 3
- Trigger GC
Output:
- Heap before GC: [1: [2], 2: [3], 3: [], 4: []]
- Heap after GC: [1: [2], 2: [3], 3: []] (Object 4 is unreachable and removed)
Explanation: Object 4 is not reachable from any root object and is therefore collected.
Example 3: (Circular Reference)
Input:
- Create objects: 1, 2
- Establish references: 1 -> 2, 2 -> 1
- Trigger GC
Output:
- Heap before GC: [1: [2], 2: [1]]
- Heap after GC: [1: [2], 2: [1]] (No objects are unreachable)
Explanation: Objects 1 and 2 form a circular reference and are both reachable.
Constraints
- The number of objects created should not exceed 100.
- The object IDs should be unique integers.
- The marking phase should be implemented using a simple depth-first search (DFS) approach.
- The sweeping phase should iterate through the heap in a linear fashion.
- The program should be reasonably efficient for the given constraints. Avoid excessive memory allocation or complex data structures.
Notes
- This is a simulation, so you don't need to implement a real memory allocator. Focus on the logic of marking and sweeping.
- Consider using a
map[int]*Objectto represent the heap for efficient object lookup. - The root objects can be hardcoded for simplicity.
- The goal is to demonstrate the core concepts of incremental GC, not to create a production-ready GC implementation.
- Think about how to represent "marked" objects. A simple boolean flag on the
Objectstruct is sufficient. - Error handling is not required for this challenge. Focus on the core GC logic.