Tag: dotnet, csharp, clr, aspnetcore, garbage-collector, async
Livello: intermedio
TL;DR: Panoramica su runtime .NET, gestione memoria con Garbage Collector, programmazione asincrona, dependency injection e ASP.NET Core per colloqui tecnici.
CLR (Common Language Runtime) gestisce esecuzione, JIT compilation, type safety e allocazione memoria.
Il Garbage Collector opera a generazioni: Gen 0 per oggetti effimeri, Gen 1 come buffer, Gen 2 per lunga persistenza.
Large Object Heap (LOH) alloca oggetti oltre gli 85.000 byte senza compattazione automatica ordinaria.
Value type allocati su stack o inline nella struct; reference type allocati sempre sull'heap gestito.
async/await sfrutta una state machine generata dal compilatore per liberare thread del threadpool durante I/O.
ConfigureAwait(false) evita la cattura del SynchronizationContext, riducendo overhead e deadlock su runtime legacy.
I/O bound tasks usano async; CPU bound tasks usano Task.Run per delegare l'elaborazione a worker thread.
Dependency Injection integrata supporta tre cicli di vita: Transient (nuovo per chiamata), Scoped (per richiesta HTTP), Singleton (unico per processo).
Kestrel e pipeline middleware di ASP.NET Core processano le richieste HTTP in sequenza bidirezionale.
Span e Memory consentono slicing di memoria contigua a zero allocazioni.
Q: Qual e' la differenza tra Task.Run e una chiamata async naturale?R: La chiamata async naturale sfrutta completamenti di I/O senza impegnare thread del pool, mentre Task.Run sottrae attivamente un worker thread per carichi CPU-bound.
Q: Quando usi Scoped rispetto a Transient nella Dependency Injection?R: Scoped per condividere lo stato nell'arco della singola richiesta HTTP come DbContext, Transient per servizi stateless leggeri e isolati.
Q: Come gestisci la finalizzazione delle risorse non gestite?R: Implemento il pattern IDisposable con Dispose per rilascio deterministico, affiancato da IAsyncDisposable se il rilascio richiede operazioni I/O.
Usare .Result o .Wait() su Task provocando threadpool starvation o deadlock.
Trattenere riferimenti a eventi statici senza unsubscribe, causando memory leak su Gen 2.
Captive Dependency: iniettare un servizio Scoped dentro un Singleton, rendendo lo Scoped un Singleton de facto.
Allocare stringhe ripetutamente in cicli invece di usare StringBuilder o Span.
Dimenticare la deregistrazione di IDisposable creati manualmente fuori dal container DI.
// Registrazione dei servizi in Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IEmailSender, EmailSender>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddSingleton<ICacheManager, CacheManager>();
var app = builder.Build();
C#
// Esecuzione asincrona pulita senza bloccare il thread
public async Task<UserProfile> GetProfileAsync(int id, CancellationToken ct)
{
var user = await _userRepo.FindByIdAsync(id, ct).ConfigureAwait(false);
return user is null ? throw new NotFoundException() : _mapper.Map(user);
}
Bash
# Comandi CLI frequenti
dotnet new webapi -n ApiProject
dotnet build --configuration Release
dotnet run --launch-profile https
[ ] Meccanismi di allocazione e soglie del Large Object Heap (LOH)
[ ] Differenza pratica tra ValueTask e Task
[ ] Pattern IAsyncDisposable e DisposeAsync
[x] Cicli di vita della Dependency Injection (Transient, Scoped, Singleton)
[x] Differenza tra struct (value type) e class (reference type)
Microsoft .NET Documentation
Common Language Runtime (CLR) Specifications