String Interning در C# چیست؟

String Interning در C# چیست؟

در خیلی از app ها اکثر داده ها از نوع string است به همین خاطر نیاز به بهینه سازی هایی برای کاهش مصرف حافظه داریم. CLR از این بهینه سازی پیشتیبانی میکنه و اسم اون رو string interning گذاشته.

CLR یک hash table داخلی به نام Intern pool داره که ارجاعات واحدی به string literal ها داریم با کمک این بهینه سازی CLR از ایجاد اشیا رشته ای با iteral مشابه جلوگیری میکنه.

در مواردی که CLR بفهمه شما دارید تلاش میکنید یک نمونه string با یک literal ایجاد کنید که از  قبل در hash table وجود داره از ارجاع مرتبط با string literal استفاده میکمنه و حافظه جدیدی تخصیص نمیده. CLR تصمیم گیری در رابطه با اینکه کدام Literal باید intern بشه و کدوم نباید بشه رو به عهده میگیره.

مثال:

string stringOne = "Hello World";
string stringTwo = "Hello World";
Console.WriteLine(object.ReferenceEquals(stringTwo, stringOne));
//true
دلیل برابر بودن ارجاع این دو متغیر این است که وقتی دستور اول اجرا میشه یک ورودی جدید در intern pool hash table با یک مرجع و یک literal اضافه میشه. زمانی که دستور دوم اجرا بشه اول داخل intern pool چک میشه که ببینیم آیا این lietral از قبل هست یا نه اگر وجود داشت ارجاع به اون برگشت داده میشه و اگر هم نبود یک سطر جدید داخل intern pool hash table ایجاد میشه. دستور دوم اجرا میشه ولی چون مقدار اون داخل intern pool پیدا میشه ارجاع جدیدی ایجاد نمیشه و در اولین باری که garbage collection اجرا بشه از حافظه پاک میشه.
 

اجرای دستی String Interning

String builder مشکل ترکیب رشته ها را با تضمین اینکه string جدیدی در زمانی ترکیب رشته ها با هم ایجاد نمیشه حل میکنه. مثال
string stringOne = "Hello World";
string stringTwo = "Hello World";
Console.WriteLine(object.ReferenceEquals(stringTwo, stringOne));
 
StringBuilder stringthree=new StringBuilder("Hello World");
StringBuilder sb=new StringBuilder();
string stringfour=sb.Append("Hello World").ToString();
 
Console.WriteLine(object.ReferenceEquals(stringOne, stringthree));
Console.WriteLine(object.ReferenceEquals(stringOne, stringfour));
 
//true
//false
//false
برای مقادیری که با string builder تولید کردیم false در خروجی مشاهده شد دلیل این است که آن هر بار که اجرا می شود یک مکان حافظه جدید بهش اختصاص میده حتی اگر مقادیر تکراری باشند.
برای حل این مشکل میتونیم به صورت دستی با استفاده از متد string.Intern عملیات intern رو انجام بدیم.

string stringOne = "Hello World";
string stringTwo = "Hello World";
Console.WriteLine(object.ReferenceEquals(stringTwo, stringOne));

StringBuilder sb = new StringBuilder("Hello World");
StringBuilder sb1 = new StringBuilder();
string stringthree = string.Intern(sb.ToString());
string stringfour = string.Intern(sb1.Append("Hello World").ToString());
Console.WriteLine(object.ReferenceEquals(stringOne, stringthree));
Console.WriteLine(object.ReferenceEquals(stringOne, stringfour));
//true
//true
//true

حالا همه string ها از مکان حافظه یکسان stringOne ایجاد شده اند. مکان حافظه ایجاد شده توسط stringTwo,stringthree, stringfour برای فرآیند garbage collection آزاد خواهند شد.

متد IsInterned

متد String.IsInterned مشخص میکنه که رشته interned شده یا خیر.

string stringOne = "Hello World";
string stringTwo = "Hello World";

StringBuilder sb = new StringBuilder("Hello World");
StringBuilder sb1 = new StringBuilder();
string stringthree = string.Intern(sb.ToString());
string stringfour = string.Intern(sb1.Append("Hello World").ToString());
Console.WriteLine(string.IsInterned(stringfour + "12"));
Console.WriteLine(string.IsInterned(stringthree));

//

//Hello World

 

فقط دستور آخر اجرا میشود و مقدار Interned شده را بر میگرداند چون در دستور اول مقدار Hello World12 در intern pool نبود و لذا یک مقدار null (فضای خالی) بر میگردونه.

نتیجه گیری

String Interning یک تمرین خوب است چون برنامه را برای استفاده از حافظه کمتر بهینه می کند. ما فقط مقادیر غیر تکراری را ارجاع میدهیم و garbage collector اشیایی که برای مقادیر تکراری ایجاد شده اند را حذف می کند.

چون ما اشیا موجود را به جای اینکه از اول ایجاد کنیم ارجاع می دهیم زمان اجرا کاهش خواهد یافت.

 

مرجع

What is String Interning in C#

اشتراک گذاری