Memory Management

RAII, ownership rules, no garbage collector, smart pointers, and lifetime patterns.

⚠ No Garbage Collector
sharp-runtime does not implement a garbage collector. Memory is managed entirely through C++ RAII, destructors, and smart pointers. The System::GC class exists as a stub/no-op compatibility header only.

Core Principle: RAII

All resource management in sharp-runtime follows C++ RAII (Resource Acquisition Is Initialization): resources are acquired in constructors and released in destructors. When a value goes out of scope or a smart pointer's reference count drops to zero, its destructor runs automatically.

This differs fundamentally from .NET, where the garbage collector handles deallocation.

Ownership Rules

PatternC++ ToolWhen to Use
Exclusive ownershipstd::unique_ptr<T>One clear owner; ownership can be transferred
Shared ownershipstd::shared_ptr<T>Multiple owners (e.g., immutable collections)
Non-owning referenceraw pointer T* or reference T&Observing only; caller guarantees lifetime
Value semanticsby-value (stack)Most primitive types, structs

Immutable Collections

Immutable collection types use shared_ptr<const std::container<T>>:

// ImmutableArray<T> is backed by shared_ptr to const vector
auto arr = ImmutableArray<int>::Create({1, 2, 3});

No GC — Practical Consequences

Null Handling

C# references can be null; C++ pointers can be nullptr. In sharp-runtime code:

Copy and Move Semantics

Most value types in sharp-runtime (structs, primitives) follow standard C++ copy/move semantics. Large types like System::Text::StringBuilder store an internal std::string buffer and support move construction and assignment.

Class types that inherit from System::Object typically have virtual destructors. They are generally heap-allocated and accessed via pointer or smart pointer.

Destructor and Cleanup Patterns

// Pattern 1: RAII — stack-allocated value type
{
    System::Text::StringBuilder sb;
    sb.Append("hello");
    // sb is destroyed when it goes out of scope
}

// Pattern 2: shared_ptr for heap-allocated Objects
auto stream = std::make_shared<System::IO::MemoryStream>();
stream->Write(buffer, 0, len);
// Destroyed when all shared_ptrs go out of scope

// Pattern 3: unique_ptr for exclusive ownership
auto reader = std::make_unique<System::IO::BinaryReader>(stream);
// Destroyed when reader goes out of scope

Differences from .NET / C# Behavior

C# / .NETsharp-runtime / C++
GC collects unreachable objectsNo GC — RAII + smart pointers
Finalizers run asynchronouslyDestructors run synchronously at scope exit
using statement calls Dispose()No using — call Dispose() manually or use RAII
Reference cycles handled by GCUse std::weak_ptr to break cycles
All objects are heap-allocatedValue types can be stack-allocated
Null reference throws NullReferenceExceptionNull dereference is UB — some places throw explicitly

Known Risks

Needs verification
The exact ownership conventions used by each subsystem (e.g., whether Stream callers own the buffer or the stream owns it) should be verified per-class from the source headers.