دادهی رشته (string) یکی از انواع دادهها در زبان برنامهنویسی جاوا است. رشته (String) دنبالهای از کاراکترها است. در جاوا اشیا کلاس string غیرقابل تغییر هستند. نوع داده string را پس از ایجاد نمیتوان تغییر داد. تمام دادههای string تعریف شده در برنامه در String Constant Pool ذخیره میشوند. در این مقاله با نحوه ذخیرهسازی دادههای string، نحوه کار String Constant Pool و فواید آن آشنا میشویم.
Java String Pool چیست؟
Java String Pool یک بخش از حافظه هیپ (heap) است. تمام رشتههای تعریف شده در برنامه در این بخش ذخیره میشوند. در ابتدا ماشین مجازی جاوا (JVM – Java Virtual Machine)، هنگام ایجاد یک رشته، وجود آن را در String Pool بررسی میکند. اگر آن رشته قبلاً در String Pool ذخیره شده باشد، متغیر جدید به همان شیء قبلی ارجاع داده میشود. اگر آن رشته وجود نداشته باشد، یک شیء جدید در String Pool ایجاد میشود.
برای آشنایی بیشتر با ماشین مجازی جاوا (JVM – Java Virtual Machine) میتوانید به مقاله « نحوه کار JVM » مراجعه کنید.
حافظه استک و هیپ
در ابتدا برای درک بهتر نحوهی کار Java String Pool بهتر است با مفاهیم حافظه استک و هیپ کمی آشنا شویم.
حافظه هیپ (Heap memory) بخشی از حافظه است که برای ذخیرهسازی دادهها به صورت دینامیک استفاده میشود. برخی ویژگیهای آن عبارتند از:
- برای دادههای پیچیده و با عمر نامشخص مناسب است.
- برنامهنویس در برخی زبانها مانند C++ مسئول تخصیص و آزادسازی حافظه است.
- دسترسی به دادهها نسبت به حافظه استک (Stack) کندتر است.
- البته در زبانهای مانند جاوا، آزادسازی حافظه به صورت خودکار انجام میشود.
حافظه استک (Stack memory) بخشی از حافظه است که برای ذخیرهسازی دادهها به صورت موقتی و با دسترسی سریع استفاده میشود. برخی ویژگیهای آن عبارتند از:
- برای ذخیره متغیرهای محلی و پارامترهای توابع استفاده میشود.
- تخصیص و آزادسازی حافظه به صورت خودکار انجام میشود.
- دسترسی به دادهها سریعتر از حافظه هیپ است.
- عمر دادهها به مدت اجرای تابع یا بلوک کد محدود است.
یک مثال از Java String Pool
دو برنامهای که با زبان جاوا در مثال اول و دوم نوشتهشده است را در نظر بگیرید. در مثال اول خروجی Yes است ولی در مثال دوم خروجی No است. فکر میکنید دلیل این تفاوت چیست؟
مثال اول با خروجی Yes:
// Program 1: Comparing two references to objects
// created using literals.
import java.util.*;
// Driver Class
class GFG {
// main function
public static void main(String[] args)
{
String s1 = "abc";
String s2 = "abc";
// Note that this == compares whether
// s1 and s2 refer to same object or not
if (s1 == s2)
System.out.println("Yes");
else
System.out.println("No");
}
}
مثال دوم با خروجی No:
// Program 2: Comparing two references to objects
// created using new operator.
import java.util.*;
// Driver Class
class GFG {
// main function
public static void main(String[] args)
{
String s1 = new String("abc");
String s2 = new String("abc");
// Note that this == compares whether
// s1 and s2 refer to same object or not
if (s1 == s2)
System.out.println("Yes");
else
System.out.println("No");
}
}
بعد از آشنایی با نحوهی کار Java String Pool به تفاوت این دو برنامه میپردازیم.
نحوهی کار Java String Pool
یکی از مهمترین ویژگیهای رشته در جاوا غیرقابل تغییر بودن است. به عبارت دیگر، زمانی که یک رشته ایجاد میشود، وضعیت داخلی آن در طول اجرای برنامه ثابت باقی میماند. این غیرقابل تغییر بودن به کمک String Constant Pool که بخشی از حافظه هیپ است، امکانپذیر میشود. وقتی یک رشته در جاوا تعریف میشود، شیء نوع string (متغیر) در حافظه استک ساخته میشود در حالی که مقدار آن رشته در حافظهی هیپ ذخیره میشود. برای درک بهتر بیایید مقداری به یک رشته str1 اختصاص دهیم.
شکل ۱ نحوهی تخصیص حافظه برای str1 را نشان میدهد.
شکل ۱.نحوهی تخصیص حافظه برای str1
همانطور که در شکل ۱ مشاهده میشود، به دلیل اینکه مقدار "Hello" را به صورت معمولی به str1 اختصاص دادهایم، متغیر در حافظه استک و مقدار "Hello" در حافظه هیپ در String Pool ذخیره شدهاست.
حال متغیر str1 و str2 را که مقادیر یکسان دارند را در نظر بگیرید.
String str1 = "Hello";
String str2 = "Hello";
شکل ۲ نحوهی تخصیص حافظه برای str1 و str2 را نشان میدهد.
شکل ۲.نحوهی تخصیص حافظه برای str1 و str2
همانطور که در شکل ۲ مشاهده میشود، هر دو متغیر str1 و str2 در حافظه استک ذخیره شدهاند. با این حال نمونه جدیدی از مقدار "Hello" در حافظه هیپ ساخته نشدهاست و از همان نمونه قبلی استفاده شده است. String Constant Pool یک کش (Cache) کوچک است که درون حافظه هیپ قرار دارد. جاوا تمام مقادیر رشتهای را که مستقیماً مقداردهی میشوند، در String Constant Pool ذخیره میکند.String Constant Pool برای کاهش مصرف حافظه و بهبود استفاده مجدد از نمونههای موجود در حافظه طراحی شده است.
اگر یک شیء رشتهای مقدار جدیدی دریافت کند، مقدار جدید به عنوان یک نمونهی جداگانه در String Constant Pool ذخیره خواهد شد.
مثال زیر را در نظر بگیرید:
String str1 = "Hello";
String str2 = "Hello";
String str3 = "Class";
تصویر زیر، نحوهی تخصیص حافظه برای str1، str2 و str3 را نشان میدهد.
شکل ۳.نحوهی تخصیص حافظه برای str1، str2 و str3
جلوگیری از تخصیص حافظه در String Pool
یکی از راههای جلوگیری از تخصیص حافظه در String Pool این است که هنگام ایجاد یک شیء رشتهای از کلمهی کلیدی new استفاده کنیم. کلمهی کلیدی new باعث میشود که همیشه یک نمونهی جدید ایجاد شود، صرفنظر از اینکه همان مقدار قبلاً استفاده شدهاست یا خیر. استفاده از new باعث میشود که نمونهی رشتهای در حافظه هیپ اما خارج از String Constant Pool ذخیره شود. این موضوع کاملاً منطقی است، زیرا در این روش مکانیزم کش (Cache) و استفادهی مجدد از نمونهها وجود ندارد.
برای مثال:
String str1 = new String("John");
String str2 = new String("Doe");
شکل ۴ نحوهی تخصیص حافظه برای str1 و str2 را نشان میدهد.
شکل ۴.نحوهی تخصیص حافظه برای str1 و str2
دلیل تفاوت نتیجه در مثال اول
حال بعد از آشنایی با String Constant Pool میتوان دلیل تفاوت در خروجی دو مثال ابتدای مقاله را توضیح داد.
مثال اول:
class GFG {
// main function
public static void main(String[] args)
{
String s1 = "abc";
String s2 = "abc";
// Note that this == compares whether
// s1 and s2 refer to same object or not
if (s1 == s2)
System.out.println("Yes");
else
System.out.println("No");
}
}
در جاوا، وقتی از عملگر == برای مقایسه استفاده میکنید، این مقایسه به مقایسه مراجع (references) به شیءها میپردازد، نه مقایسه خود مقادیر یا محتوای آنها. این یعنی شما در حال مقایسه این هستید که آیا دو متغیر به همان شیء در حافظه اشاره میکنند یا نه.
در مثال اول هر دو متغیر str1 و str2 به یک شیء در String Pool اشاره میکنند. به همین دلیل، عملگر == برای اینها true را برمیگرداند، چون هر دو به همان آدرس حافظه (شیء مشابه) اشاره میکنند.
مثال دوم:
import java.util.*;
// Driver Class
class GFG {
// main function
public static void main(String[] args)
{
String s1 = new String("abc");
String s2 = new String("abc");
// Note that this == compares whether
// s1 and s2 refer to same object or not
if (s1 == s2)
System.out.println("Yes");
else
System.out.println("No");
}
}
در مثال دوم به دلیل وجود کلمهی کلیدی new برای هر کدارم از دو متغیر یک شیء جدید از نوع رشته در حافظه هیپ ایجاد میشود (نه در String Pool).
در این حالت str1 و str2 به دو شیء مختلف در حافظه اشاره میکنند، حتی اگر محتویات آنها (یعنی "abc") مشابه باشد. در نتیجه، str1 == str2 برابر با false خواهد بود، چون مراجع آنها متفاوت است.
نتیجهگیری
- String Constant Pool باعث کاهش مصرف حافظه و بهبود عملکرد میشود.
- اگر از کلمهی کلیدی new برای ایجاد String استفاده شود، شیء جدیدی در حافظهی هیپ ایجاد میشود، نه در String Pool.
- عملگر == فقط مراجع حافظه را مقایسه میکند، بنابراین مقدار true فقط زمانی برمیگردد که هر دو متغیر به یک آدرس حافظه اشاره کنند.
- String Constant Pool در جاوا باعث استفادهی مجدد از مقادیر مشابه و کاهش تعداد اشیای غیرضروری در حافظه میشود.
نظرات 0
برای ارسال نظر وارد حساب کاربری شوید.
هنوز نظری ثبت نشده؛ اولین نفر باشید.