Bagikan melalui


Menghosting dan menyebarkan aplikasi sisi Blazor server

Catatan

Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Peringatan

Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Penting

Informasi ini berkaitan dengan produk pra-rilis yang mungkin dimodifikasi secara substansial sebelum dirilis secara komersial. Microsoft tidak memberikan jaminan, tersirat maupun tersurat, sehubungan dengan informasi yang diberikan di sini.

Untuk rilis saat ini, lihat versi .NET 8 dari artikel ini.

Artikel ini menjelaskan cara menghosting dan menyebarkan aplikasi sisi Blazor server (Blazor Web Apps dan Blazor Server aplikasi) menggunakan ASP.NET Core.

Nilai konfigurasi host

Aplikasi sisi Blazor server dapat menerima nilai konfigurasi Host Generik.

Penyebaran

Menggunakan model hosting sisi server, Blazor dijalankan di server dari dalam aplikasi ASP.NET Core. Pembaruan UI, penanganan peristiwa, dan panggilan JavaScript ditangani melalui SignalR koneksi.

Server web yang mampu menghosting aplikasi ASP.NET Core diperlukan. Visual Studio menyertakan templat proyek aplikasi sisi server. Untuk informasi selengkapnya tentang Blazor templat proyek, lihat struktur proyek ASP.NET CoreBlazor.

Terbitkan aplikasi dalam Konfigurasi rilis bin/Release/{TARGET FRAMEWORK}/publish dan sebarkan konten folder, di mana {TARGET FRAMEWORK} tempat penampung adalah kerangka kerja target.

Skalabilitas

Saat mempertimbangkan skalabilitas satu server (peningkatan skala), memori yang tersedia untuk aplikasi kemungkinan adalah sumber daya pertama yang habis saat permintaan pengguna meningkat. Memori yang tersedia di server memengaruhi:

  • Jumlah sirkuit aktif yang dapat didukung server.
  • Latensi UI pada klien.

Untuk panduan tentang membangun aplikasi sisi Blazor server yang aman dan dapat diskalakan, lihat sumber daya berikut:

Setiap sirkuit menggunakan sekitar 250 KB memori untuk aplikasi gaya Halo Dunia minimal. Ukuran sirkuit tergantung pada kode aplikasi dan persyaratan pemeliharaan status yang terkait dengan setiap komponen. Kami menyarankan agar Anda mengukur tuntutan sumber daya selama pengembangan untuk aplikasi dan infrastruktur Anda, tetapi garis besar berikut dapat menjadi titik awal dalam merencanakan target penyebaran Anda: Jika Anda mengharapkan aplikasi Anda mendukung 5.000 pengguna bersamaan, pertimbangkan untuk menganggarkan setidaknya 1,3 GB memori server ke aplikasi (atau ~273 KB per pengguna).

SignalR konfigurasi

SignalRKondisi hosting dan penskalakan berlaku untuk Blazor aplikasi yang menggunakan SignalR.

Untuk informasi selengkapnya tentang SignalR di Blazor aplikasi, termasuk panduan konfigurasi, lihat panduan ASP.NET CoreBlazorSignalR.

Transportasi

Blazorbekerja paling baik saat menggunakan WebSocket sebagai SignalR transportasi karena latensi yang lebih rendah, keandalan yang lebih baik, dan keamanan yang ditingkatkan. Polling Panjang digunakan oleh SignalR saat WebSockets tidak tersedia atau ketika aplikasi secara eksplisit dikonfigurasi untuk menggunakan Long Polling.

Peringatan konsol muncul jika Long Polling digunakan:

Gagal tersambung melalui WebSockets, menggunakan transportasi fallback Long Polling. Ini mungkin karena VPN atau proksi memblokir koneksi.

Penyebaran global dan kegagalan koneksi

Rekomendasi untuk penyebaran global ke pusat data geografis:

  • Sebarkan aplikasi ke wilayah tempat sebagian besar pengguna berada.
  • Pertimbangkan peningkatan latensi untuk lalu lintas di seluruh benua. Untuk mengontrol tampilan antarmuka pengguna koneksi ulang, lihat panduan ASP.NET CoreBlazorSignalR.
  • Pertimbangkan untuk menggunakan Layanan AzureSignalR.

Azure App Service

Hosting di Azure App Service memerlukan konfigurasi untuk WebSocket dan afinitas sesi, juga disebut afinitas Application Request Routing (ARR).

Catatan

Aplikasi Blazor di Azure App Service tidak memerlukan Azure SignalR Service.

Aktifkan yang berikut ini untuk pendaftaran aplikasi di Azure App Service:

  • WebSocket untuk memungkinkan transportasi WebSocket berfungsi. Pengaturan default nonaktif.
  • Afinitas sesi untuk merutekan permintaan dari pengguna kembali ke instans App Service yang sama. Pengaturan defaultnya adalah Aktif.
  1. Di portal Azure, navigasikan ke aplikasi web di App Services.
  2. Buka Konfigurasi Pengaturan>.
  3. Atur soket Web ke Aktif.
  4. Verifikasi bahwa Afinitas sesi diatur ke Aktif.

Layanan Azure SignalR

Layanan Azure SignalR opsional berfungsi bersama dengan hub aplikasi SignalR untuk meningkatkan skala aplikasi sisi server ke sejumlah besar koneksi bersamaan. Selain itu, jangkauan global layanan dan pusat data berkinerja tinggi secara signifikan membantu mengurangi latensi karena geografi.

Layanan ini tidak diperlukan untuk Blazor aplikasi yang dihosting di Azure App Service atau Azure Container Apps tetapi dapat membantu di lingkungan hosting lainnya:

  • Untuk memfasilitasi peluasan skala koneksi.
  • Menangani distribusi global.

Catatan

Koneksi ulang stateful (WithStatefulReconnect) dirilis dengan .NET 8 tetapi saat ini tidak didukung untuk Layanan Azure SignalR . Untuk informasi selengkapnya, lihat Dukungan Sambungkan Ulang Stateful? (Azure/azure-signalr #1878).

Jika aplikasi menggunakan Long Polling atau kembali ke Long Polling alih-alih WebSocket, Anda mungkin perlu mengonfigurasi interval polling maksimum (MaxPollIntervalInSeconds, default: 5 detik, batas: 1-300 detik), yang menentukan interval polling maksimum yang diizinkan untuk koneksi Long Polling di Azure SignalR Service. Jika permintaan polling berikutnya tidak tiba dalam interval polling maksimum, layanan menutup koneksi klien.

Untuk panduan tentang cara menambahkan layanan sebagai dependensi ke penyebaran produksi, lihat Menerbitkan aplikasi ASP.NET Core SignalR ke Azure App Service.

Untuk informasi selengkapnya, lihat:

Azure Container Apps

Untuk eksplorasi yang lebih dalam tentang penskalaan aplikasi sisi Blazor server di layanan Azure Container Apps, lihat Penskalaan ASP.NET Core Apps di Azure. Tutorial ini menjelaskan cara membuat dan mengintegrasikan layanan yang diperlukan untuk menghosting aplikasi di Azure Container Apps. Langkah-langkah dasar juga disediakan di bagian ini.

  1. Konfigurasikan layanan Azure Container Apps untuk afinitas sesi dengan mengikuti panduan dalam Afinitas Sesi di Azure Container Apps (dokumentasi Azure).

  2. Layanan ASP.NET Core Data Protection (DP) harus dikonfigurasi untuk mempertahankan kunci di lokasi terpusat yang dapat diakses oleh semua instans kontainer. Kunci dapat disimpan di Azure Blob Storage dan dilindungi dengan Azure Key Vault. Layanan DP menggunakan kunci untuk mendeserialisasi Razor komponen. Untuk mengonfigurasi layanan DP untuk menggunakan Azure Blob Storage dan Azure Key Vault, referensikan paket NuGet berikut:

    Catatan

    Untuk panduan tentang menambahkan paket ke aplikasi .NET, lihat artikel di bagian Menginstal dan mengelola paket di Alur kerja konsumsi paket (dokumentasi NuGet). Konfirmasikan versi paket yang benar di NuGet.org.

  3. Perbarui Program.cs dengan kode yang disorot berikut:

    using Azure.Identity;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.Extensions.Azure;
    
    var builder = WebApplication.CreateBuilder(args);
    var BlobStorageUri = builder.Configuration["AzureURIs:BlobStorage"];
    var KeyVaultURI = builder.Configuration["AzureURIs:KeyVault"];
    
    builder.Services.AddRazorPages();
    builder.Services.AddHttpClient();
    builder.Services.AddServerSideBlazor();
    
    builder.Services.AddAzureClientsCore();
    
    builder.Services.AddDataProtection()
                    .PersistKeysToAzureBlobStorage(new Uri(BlobStorageUri),
                                                    new DefaultAzureCredential())
                    .ProtectKeysWithAzureKeyVault(new Uri(KeyVaultURI),
                                                    new DefaultAzureCredential());
    var app = builder.Build();
    
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    
    app.UseRouting();
    
    app.UseAuthorization();
    
    app.MapRazorPages();
    
    app.Run();
    

    Perubahan sebelumnya memungkinkan aplikasi mengelola layanan DP menggunakan arsitektur terpusat dan dapat diskalakan. DefaultAzureCredential menemukan aplikasi kontainer yang dikelola identity setelah kode disebarkan ke Azure dan menggunakannya untuk menyambungkan ke penyimpanan blob dan brankas kunci aplikasi.

  4. Untuk membuat aplikasi kontainer yang dikelola identity dan memberinya akses ke penyimpanan blob dan brankas kunci, selesaikan langkah-langkah berikut:

    1. Di Portal Microsoft Azure, buka halaman gambaran umum aplikasi kontainer.
    2. Pilih Konektor Layanan dari navigasi kiri.
    3. Pilih + Buat dari navigasi atas.
    4. Di menu Buat flyout koneksi, masukkan nilai berikut ini:
      • Kontainer: Pilih aplikasi kontainer yang Anda buat untuk menghosting aplikasi Anda.
      • Jenis layanan: Pilih Blob Storage.
      • Langganan: Pilih langganan yang memiliki aplikasi kontainer.
      • Nama koneksi: Masukkan nama scalablerazorstorage.
      • Jenis klien: Pilih .NET lalu pilih Berikutnya.
    5. Pilih Sistem yang ditetapkan terkelola identity dan pilih Berikutnya.
    6. Gunakan pengaturan jaringan default dan pilih Berikutnya.
    7. Setelah Azure memvalidasi pengaturan, pilih Buat.

    Ulangi pengaturan sebelumnya untuk brankas kunci. Pilih layanan dan kunci brankas kunci yang sesuai di tab Dasar .

IIS

Saat menggunakan IIS, aktifkan:

Untuk informasi selengkapnya, lihat panduan dan tautan silang sumber daya IIS eksternal di Menerbitkan aplikasi ASP.NET Core ke IIS.

Kubernetes

Buat definisi ingress dengan anotasi Kubernetes berikut untuk afinitas sesi:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: <ingress-name>
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/session-cookie-name: "affinity"
    nginx.ingress.kubernetes.io/session-cookie-expires: "14400"
    nginx.ingress.kubernetes.io/session-cookie-max-age: "14400"

Linux dengan Nginx

Ikuti panduan untuk aplikasi ASP.NET Core SignalR dengan perubahan berikut:

  • location Ubah jalur dari /hubroute (location /hubroute { ... }) ke jalur / akar (location / { ... }).
  • Hapus konfigurasi untuk buffering proksi (proxy_buffering off;) karena pengaturan hanya berlaku untuk Peristiwa Terkirim Server (SSE), yang tidak relevan dengan Blazor interaksi server klien aplikasi.

Untuk informasi selengkapnya dan panduan konfigurasi, lihat sumber daya berikut:

Linux dengan Apache

Untuk menghosting Blazor aplikasi di belakang Apache di Linux, konfigurasikan ProxyPass untuk lalu lintas HTTP dan WebSockets.

Dalam contoh berikut:

  • Kestrel server berjalan pada komputer host.
  • Aplikasi ini mendengarkan lalu lintas di port 5000.
ProxyPreserveHost   On
ProxyPassMatch      ^/_blazor/(.*) http://localhost:5000/_blazor/$1
ProxyPass           /_blazor ws://localhost:5000/_blazor
ProxyPass           / http://localhost:5000/
ProxyPassReverse    / http://localhost:5000/

Aktifkan modul berikut:

a2enmod   proxy
a2enmod   proxy_wstunnel

Periksa konsol browser untuk kesalahan WebSockets. Contoh kesalahan:

  • Firefox tidak dapat membuat koneksi ke server di ws://the-domain-name.tld/_blazor?id=XXX
  • Kesalahan: Gagal memulai transportasi 'WebSockets': Kesalahan: Terjadi kesalahan dengan transportasi.
  • Kesalahan: Gagal memulai transportasi 'LongPolling': TypeError: this.transport tidak terdefinisi
  • Kesalahan: Tidak dapat tersambung ke server dengan salah satu transportasi yang tersedia. WebSocket gagal
  • Kesalahan: Tidak dapat mengirim data jika koneksi tidak berada dalam Status 'Tersambung'.

Untuk informasi selengkapnya dan panduan konfigurasi, lihat sumber daya berikut:

Mengukur latensi jaringan

JS interop dapat digunakan untuk mengukur latensi jaringan, seperti yang ditunjukkan contoh berikut.

MeasureLatency.razor:

@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

<h2>Measure Latency</h2>

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}
@inject IJSRuntime JS

@if (latency is null)
{
    <span>Calculating...</span>
}
else
{
    <span>@(latency.Value.TotalMilliseconds)ms</span>
}

@code {
    private DateTime startTime;
    private TimeSpan? latency;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            startTime = DateTime.UtcNow;
            var _ = await JS.InvokeAsync<string>("toString");
            latency = DateTime.UtcNow - startTime;
            StateHasChanged();
        }
    }
}

Untuk pengalaman UI yang wajar, kami merekomendasikan latensi UI berkelanjutan sebesar 250 mdtk atau kurang.

Manajemen memori

Di server, sirkuit baru dibuat untuk setiap sesi pengguna. Setiap sesi pengguna sesuai dengan penyajian satu dokumen di browser. Misalnya, beberapa tab membuat beberapa sesi.

Blazor mempertahankan koneksi konstan ke browser, yang disebut sirkuit, yang memulai sesi. Koneksi dapat hilang kapan saja karena beberapa alasan, seperti ketika pengguna kehilangan konektivitas jaringan atau menutup browser secara tiba-tiba. Ketika koneksi hilang, Blazor memiliki mekanisme pemulihan yang menempatkan sejumlah sirkuit terbatas dalam kumpulan "terputus", memberi klien waktu terbatas untuk menyambungkan kembali dan membangun kembali sesi (default: 3 menit).

Setelah itu, Blazor merilis sirkuit dan membuang sesi. Sejak saat itu, sirkuit memenuhi syarat untuk pengumpulan sampah (GC) dan diklaim ketika koleksi untuk generasi GC sirkuit dipicu. Salah satu aspek penting untuk dipahami adalah bahwa sirkuit memiliki masa pakai yang panjang, yang berarti bahwa sebagian besar objek yang diakar oleh sirkuit akhirnya mencapai Gen 2. Akibatnya, Anda mungkin tidak melihat objek tersebut dirilis hingga koleksi Gen 2 terjadi.

Mengukur penggunaan memori secara umum

Prasyarat:

  • Aplikasi harus diterbitkan dalam konfigurasi Rilis . Pengukuran konfigurasi debug tidak relevan, karena kode yang dihasilkan tidak mewakili kode yang digunakan untuk penyebaran produksi.
  • Aplikasi harus berjalan tanpa debugger terlampir, karena ini mungkin juga memengaruhi perilaku aplikasi dan merusak hasilnya. Di Visual Studio, mulai aplikasi tanpa penelusuran kesalahan dengan memilih Debug>Mulai Tanpa Debugging dari bilah menu atau Ctrl+F5 menggunakan keyboard.
  • Pertimbangkan berbagai jenis memori untuk memahami berapa banyak memori yang sebenarnya digunakan oleh .NET. Umumnya, pengembang memeriksa penggunaan memori aplikasi di Task Manager pada OS Windows, yang biasanya menawarkan batas atas memori aktual yang digunakan. Untuk informasi selengkapnya, lihat artikel berikut:

Penggunaan memori diterapkan ke Blazor

Kami menghitung memori yang digunakan oleh blazor sebagai berikut:

(Sirkuit Aktif × Memori Per sirkuit) + (Sirkuit Terputus × Memori Per sirkuit)

Jumlah memori yang digunakan sirkuit dan potensi maksimum sirkuit aktif yang dapat dipertahankan aplikasi sangat tergantung pada cara aplikasi ditulis. Jumlah maksimum kemungkinan sirkuit aktif kira-kira dijelaskan oleh:

Memori Maksimum yang / Tersedia Per Sirkuit Memori = Maksimum PotensiAl Sirkuit Aktif

Agar kebocoran memori terjadi di Blazor, hal berikut harus benar:

  • Memori harus dialokasikan oleh kerangka kerja, bukan aplikasi. Jika Anda mengalokasikan array 1 GB di aplikasi, aplikasi harus mengelola pembuangan array.
  • Memori tidak boleh digunakan secara aktif, yang berarti sirkuit tidak aktif dan telah dikeluarkan dari cache sirkuit yang terputus. Jika Anda memiliki sirkuit aktif maksimum yang berjalan, kehabisan memori adalah masalah skala, bukan kebocoran memori.
  • Pengumpul sampah (GC) untuk generasi GC sirkuit telah berjalan, tetapi pengumpul sampah belum dapat mengklaim sirkuit karena objek lain dalam kerangka kerja memegang referensi yang kuat ke sirkuit.

Dalam kasus lain, tidak ada kebocoran memori. Jika sirkuit aktif (tersambung atau terputus), sirkuit masih digunakan.

Jika koleksi untuk generasi GC sirkuit tidak berjalan, memori tidak dirilis karena pengumpul sampah tidak perlu membebaskan memori pada saat itu.

Jika koleksi untuk generasi GC berjalan dan membebaskan sirkuit, Anda harus memvalidasi memori terhadap statistik GC, bukan prosesnya, karena .NET mungkin memutuskan untuk menjaga memori virtual tetap aktif.

Jika memori tidak dibesarkan, Anda harus menemukan sirkuit yang tidak aktif atau terputus dan itu berakar oleh objek lain dalam kerangka kerja. Dalam kasus lain, ketidakmampuan untuk mengosongkan memori adalah masalah aplikasi dalam kode pengembang.

Mengurangi penggunaan memori

Adopsi salah satu strategi berikut untuk mengurangi penggunaan memori aplikasi:

  • Batasi jumlah total memori yang digunakan oleh proses .NET. Untuk informasi selengkapnya, lihat Opsi konfigurasi runtime untuk pengumpulan sampah.
  • Kurangi jumlah sirkuit yang terputus.
  • Kurangi waktu sirkuit diizinkan untuk berada dalam status terputus.
  • Picu pengumpulan sampah secara manual untuk melakukan pengumpulan selama periode waktu henti.
  • Konfigurasikan pengumpulan sampah dalam mode Stasiun Kerja, yang secara agresif memicu pengumpulan sampah, bukan mode Server.

Ukuran timbunan untuk beberapa browser perangkat seluler

Saat membuat Blazor aplikasi yang berjalan pada klien dan menargetkan browser perangkat seluler, terutama Safari di iOS, mengurangi memori maksimum untuk aplikasi dengan properti EmccMaximumHeapSize MSBuild mungkin diperlukan. Untuk informasi selengkapnya, lihat Host dan sebarkan ASP.NET Core Blazor WebAssembly.

Tindakan dan pertimbangan tambahan

  • Ambil cadangan memori proses ketika tuntutan memori tinggi dan identifikasi objek mengambil memori paling banyak dan di mana objek tersebut berakar (apa yang menyimpan referensi untuk mereka).
  • Anda dapat memeriksa statistik tentang bagaimana memori di aplikasi Anda berulah menggunakan dotnet-counters. Untuk informasi selengkapnya, lihat Menyelidiki penghitung kinerja (penghitung dotnet).
  • Bahkan ketika GC dipicu, .NET berpegang pada memori alih-alih segera mengembalikannya ke OS, karena kemungkinan akan menggunakan kembali memori dalam waktu dekat. Ini menghindari penerapan dan penolakan memori terus-menerus, yang mahal. Anda akan melihat ini tercermin jika Anda menggunakan dotnet-counters karena Anda akan melihat GC terjadi dan jumlah memori yang digunakan turun ke 0 (nol), tetapi Anda tidak akan melihat penurunan penghitung set kerja, yang merupakan tanda bahwa .NET memegang memori untuk menggunakannya kembali. Untuk informasi selengkapnya tentang pengaturan file proyek (.csproj) untuk mengontrol perilaku ini, lihat Opsi konfigurasi runtime untuk pengumpulan sampah.
  • Server GC tidak memicu pengumpulan sampah sampai menentukan benar-benar perlu untuk melakukannya untuk menghindari pembekuan aplikasi Anda dan menganggap bahwa aplikasi Anda adalah satu-satunya hal yang berjalan di komputer, sehingga dapat menggunakan semua memori dalam sistem. Jika sistem memiliki 50 GB, pengumpul sampah berusaha menggunakan memori 50 GB penuh yang tersedia sebelum memicu koleksi Gen 2.
  • Untuk informasi tentang konfigurasi retensi sirkuit yang terputus, lihat panduan ASP.NET CoreBlazorSignalR.

Mengukur memori

  • Terbitkan aplikasi dalam Konfigurasi rilis.
  • Jalankan versi aplikasi yang diterbitkan.
  • Jangan lampirkan debugger ke aplikasi yang sedang berjalan.
  • Apakah memicu koleksi gen 2 yang dipaksakan dan memadatkan (GC.Collect(2, GCCollectionMode.Aggressive | GCCollectionMode.Forced, blocking: true, compacting: true)) bebaskan memori?
  • Pertimbangkan apakah aplikasi Anda mengalokasikan objek pada timbunan objek besar.
  • Apakah Anda menguji pertumbuhan memori setelah aplikasi dihangatkan dengan permintaan dan pemrosesan? Biasanya, ada cache yang diisi saat kode dijalankan untuk pertama kalinya yang menambahkan jumlah memori konstan ke jejak aplikasi.