Apa sajakah tip untuk mengurangi penggunaan memori aplikasi .NET? Pertimbangkan program C # sederhana berikut.
class Program
{
static void Main(string[] args)
{
Console.ReadLine();
}
}
Dikompilasi dalam mode rilis untuk x64 dan berjalan di luar Visual Studio, pengelola tugas melaporkan hal berikut:
Working Set: 9364k
Private Working Set: 2500k
Commit Size: 17480k
Sedikit lebih baik jika dikompilasi hanya untuk x86 :
Working Set: 5888k
Private Working Set: 1280k
Commit Size: 7012k
Saya kemudian mencoba program berikut, yang melakukan hal yang sama tetapi mencoba untuk memangkas ukuran proses setelah inisialisasi runtime:
class Program
{
static void Main(string[] args)
{
minimizeMemory();
Console.ReadLine();
}
private static void minimizeMemory()
{
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
(UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetProcessWorkingSetSize(IntPtr process,
UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
}
Hasil pada x86 Rilis di luar Visual Studio:
Working Set: 2300k
Private Working Set: 964k
Commit Size: 8408k
Mana yang sedikit lebih baik, tetapi masih tampak berlebihan untuk program sederhana seperti itu. Apakah ada trik untuk membuat proses C # sedikit lebih ramping? Saya sedang menulis program yang dirancang untuk berjalan di latar belakang hampir sepanjang waktu. Saya sudah melakukan hal-hal antarmuka pengguna di Domain Aplikasi terpisah yang berarti hal-hal antarmuka pengguna dapat dibongkar dengan aman, tetapi menghabiskan 10 MB ketika hanya duduk di latar belakang tampaknya berlebihan.
NB Tentang mengapa saya akan peduli --- Pengguna (Daya) cenderung khawatir tentang hal-hal ini. Bahkan jika itu hampir tidak berpengaruh pada kinerja, pengguna semi-tech-savvy (audiens target saya) cenderung masuk ke hissy fit tentang penggunaan memori aplikasi latar belakang. Bahkan saya merasa aneh ketika melihat Adobe Updater menggunakan memori 11 MB dan merasa terhibur dengan sentuhan menenangkan dari Foobar2000, yang dapat berukuran di bawah 6 MB bahkan saat bermain. Saya tahu dalam sistem operasi modern, hal-hal ini sebenarnya tidak terlalu penting secara teknis, tetapi itu tidak berarti tidak memengaruhi persepsi.
sumber
Jawaban:
Saya tidak akan mengatakan bahwa Anda harus mengabaikan jejak memori aplikasi Anda - jelas, lebih kecil dan lebih efisien memang cenderung diinginkan. Namun, Anda harus mempertimbangkan kebutuhan Anda yang sebenarnya.
Jika Anda menulis aplikasi klien Windows Forms dan WPF standar yang ditakdirkan untuk dijalankan pada PC individu, dan kemungkinan besar merupakan aplikasi utama tempat pengguna beroperasi, Anda dapat menghindari menjadi lebih lesu tentang alokasi memori. (Selama semuanya dibatalkan alokasinya.)
Namun, untuk menjawab beberapa orang di sini yang mengatakan tidak perlu khawatir tentang itu: Jika Anda menulis aplikasi Windows Forms yang akan berjalan di lingkungan layanan terminal, di server bersama yang mungkin digunakan oleh 10, 20 atau lebih pengguna, maka ya , Anda benar-benar harus mempertimbangkan penggunaan memori. Dan Anda harus waspada. Cara terbaik untuk mengatasinya adalah dengan desain struktur data yang baik dan dengan mengikuti praktik terbaik terkait waktu dan apa yang Anda alokasikan.
sumber
Aplikasi .NET akan memiliki footprint yang lebih besar dibandingkan dengan aplikasi asli karena keduanya harus memuat runtime dan aplikasi dalam prosesnya. Jika Anda menginginkan sesuatu yang sangat rapi, .NET mungkin bukan pilihan terbaik.
Namun, perlu diingat bahwa jika aplikasi Anda sebagian besar tertidur, halaman memori yang diperlukan akan ditukar dari memori dan dengan demikian tidak terlalu menjadi beban sistem pada umumnya.
Jika Anda ingin membuat footprint kecil, Anda harus memikirkan penggunaan memori. Berikut beberapa ide:
List<T>
dan jenis serupa yang menggandakan kapasitas saat dibutuhkan karena dapat menyebabkan hingga 50% limbah.Itu mungkin bukan daftar lengkap dengan cara apa pun, tetapi hanya beberapa ide.
sumber
Satu hal yang perlu Anda pertimbangkan dalam hal ini adalah biaya memori CLR. CLR dimuat untuk setiap proses .Net dan karenanya menjadi faktor pertimbangan memori. Untuk program yang sederhana / kecil, biaya CLR akan mendominasi jejak memori Anda.
Akan jauh lebih instruktif untuk membuat aplikasi nyata dan melihat biayanya dibandingkan dengan biaya program baseline ini.
sumber
Tidak ada saran khusus, tetapi Anda dapat melihat CLR Profiler (unduh gratis dari Microsoft).
Setelah Anda menginstalnya, lihat halaman petunjuk ini .
Dari cara-untuk:
sumber
Mungkin ingin melihat penggunaan memori dari aplikasi "nyata".
Mirip dengan Java, ada sejumlah overhead tetap untuk runtime terlepas dari ukuran programnya, tetapi konsumsi memori akan jauh lebih masuk akal setelah titik itu.
sumber
Masih ada cara untuk mengurangi set kerja privat dari program sederhana ini:
NGEN aplikasi Anda. Ini menghapus biaya kompilasi JIT dari proses Anda.
Latih aplikasi Anda menggunakan MPGO yang mengurangi penggunaan memori dan kemudian NGEN.
sumber
Ada banyak cara untuk mengurangi jejak kaki Anda.
Satu hal yang harus selalu Anda lakukan di .NET adalah ukuran gambar asli kode IL Anda sangat besar
Dan kode ini tidak dapat dibagikan sepenuhnya di antara instance aplikasi. Bahkan rakitan NGEN tidak sepenuhnya statis, mereka masih memiliki beberapa bagian kecil yang perlu JITting.
Orang juga cenderung menulis kode yang memblokir memori jauh lebih lama dari yang diperlukan.
Contoh yang sering terlihat: Mengambil Datareader, memuat konten ke DataTable hanya untuk menulisnya ke dalam file XML. Anda dapat dengan mudah mengalami OutOfMemoryException. OTOH, Anda dapat menggunakan XmlTextWriter dan menelusuri Datareader, memancarkan XmlNodes saat Anda menggulir kursor database. Dengan begitu, Anda hanya memiliki catatan database saat ini dan keluaran XML-nya di memori. Yang tidak akan pernah (atau tidak mungkin) mendapatkan generasi pengumpulan sampah yang lebih tinggi dan dengan demikian dapat digunakan kembali.
Hal yang sama berlaku untuk mendapatkan daftar beberapa contoh, melakukan beberapa hal (yang memunculkan ribuan contoh baru, yang mungkin tetap dirujuk di suatu tempat), dan meskipun Anda tidak membutuhkannya setelah itu, Anda masih mereferensikan semuanya sampai setelah pendahuluan. Secara eksplisit membatalkan daftar input Anda dan produk sampingan sementara Anda berarti, memori ini dapat digunakan kembali bahkan sebelum Anda keluar dari loop Anda.
C # memiliki fitur unggulan yang disebut iterator. Mereka memungkinkan Anda untuk mengalirkan objek dengan menggulir melalui masukan Anda dan hanya menyimpan contoh saat ini sampai Anda mendapatkan yang berikutnya. Bahkan dengan menggunakan LINQ, Anda tetap tidak perlu menyimpan semuanya hanya karena Anda ingin memfilternya.
sumber
Mengatasi pertanyaan umum dalam judul dan bukan pertanyaan spesifik:
Jika Anda menggunakan komponen COM yang mengembalikan banyak data (katakanlah array 2xN besar ganda) dan hanya sebagian kecil yang dibutuhkan maka seseorang dapat menulis komponen COM pembungkus yang menyembunyikan memori dari .NET dan hanya mengembalikan data yang dibutuhkan.
Itulah yang saya lakukan di aplikasi utama saya dan secara signifikan meningkatkan konsumsi memori.
sumber
Saya telah menemukan bahwa menggunakan API SetProcessWorkingSetSize atau EmptyWorkingSet untuk memaksa halaman memori ke disk secara berkala dalam proses yang berjalan lama dapat mengakibatkan semua memori fisik yang tersedia pada mesin menghilang secara efektif hingga mesin di-boot ulang. Kami memiliki DLL .NET yang dimuat ke dalam proses asli yang akan menggunakan EmptyWorkingSet API (alternatif untuk menggunakan SetProcessWorkingSetSize) untuk mengurangi set kerja setelah melakukan tugas intensif memori. Saya menemukan bahwa setelah antara 1 hari hingga seminggu, mesin akan menunjukkan 99% penggunaan memori fisik di Task Manager sementara tidak ada proses yang terbukti menggunakan penggunaan memori yang signifikan. Segera setelah mesin menjadi tidak responsif, membutuhkan booting ulang yang sulit. Mesin tersebut lebih dari 2 lusin server Windows Server 2008 R2 dan 2012 R2 yang berjalan pada perangkat keras fisik dan virtual.
Mungkin memiliki kode .NET yang dimuat ke dalam proses asli ada hubungannya dengan itu, tetapi gunakan EmptyWorkingSet (atau SetProcessWorkingSetSize) dengan risiko Anda sendiri. Mungkin hanya menggunakannya sekali setelah peluncuran awal aplikasi Anda. Saya telah memutuskan untuk menonaktifkan kode dan membiarkan Pengumpul Sampah mengelola penggunaan memori sendiri.
sumber