1. الحد الأعلى لتخصيص الصفيف
حجم صفيف في Java محدود لأنه يستخدم نوع int كـ Array Corrcs. هذا يعني أنه لا يمكنك التقدم بطلب للحصول على المصفوفات التي تتجاوز حجم integer.max_value (2^31-1). هذا لا يعني أن الحد الأعلى لتطبيق الذاكرة الخاص بك هو 2G. يمكنك التقدم بطلب للحصول على مجموعة من الأنواع الكبيرة. على سبيل المثال:
نسخة الكود كما يلي:
Final Long [] Ar = New Long [integer.max_value] ؛
سيؤدي ذلك إلى تخصيص 16G -8 بايت. هو مجرد قواعد عامة ، يعتمد التخصيص المحدد على الوضع الفعلي).
لسوء الحظ ، في Java ، نظرًا لنوع الحد من عناصر الصفيف ، سيكون من المقلق أكثر تشغيل الذاكرة. عندما يتعلق الأمر بصفائف التشغيل ، يجب أن يكون Bytebuffer الفئة الأكثر فائدة ، والتي توفر طرقًا لقراءة وكتابة أنواع Java المختلفة. عيبه هو أن نوع الصفيف المستهدف يجب أن يكون بايت [] ، مما يعني أن ذاكرة التخزين المؤقت للذاكرة القصوى التي تخصيتها يمكن أن تكون 2G.
2. استخدم جميع المصفوفات كمصفوفات بايت للعمل
على افتراض أن ذاكرة 2G أبعد ما يكون عننا الآن ، فلا بأس إذا كان 16 جرام. لقد قمنا بتعيين []] ، لكننا نريد تشغيله كصفيف بايت. في Java ، علينا أن نطلب المساعدة من مبرمجي C - Sun.Misc.unsafe. تحتوي هذه الفئة على مجموعتين من الطرق: GETN (الكائن ، الإزاحة) ، هذه الطريقة هي الحصول على قيمة للنوع المحدد من الموضع حيث تم إزاحة الكائن وإعادته هنا PUTN (الكائن ، الإزاحة ، القيمة) هي كتابة قيمة إلى موضع إزاحة الكائن.
لسوء الحظ ، يمكن لهذه الطرق الحصول على قيم أو تعيين قيم لنوع معين فقط. إذا قمت بنسخ البيانات من صفيف ، فأنت بحاجة أيضًا إلى طريقة أخرى من copymemory غير الآمنة (SRCOBject ، SRCOFFSET ، DESTOBJECT ، DESTOFFET ، COUNT). هذا يعمل بشكل مشابه للنظام.
للوصول إلى بيانات صفيف من خلال sun.misc.unsafe ، تحتاج إلى شيئين:
1. إزاحة البيانات في كائن الصفيف
2. إزاحة العناصر النسخ في بيانات الصفيف
مثل كائنات Java الأخرى ، تحتوي المصفوفات على رأس كائن ، يتم تخزينه أمام البيانات الفعلية. يمكن الحصول على طول هذا الرأس بواسطة طريقة غير آمنة. يمكن الحصول على حجم عنصر الصفيف من خلال طريقة غير آمنة. هذا يعني أنه إذا كنت ترغب في الوصول إلى العنصر التاسع للنوع T ، فيجب أن يكون إزاحة الإزاحة الخاصة بك هو ArrayOffset+N*صفائف.
دعونا نكتب مثالًا بسيطًا. نخصص صفيفًا طويلًا ونقوم بتحديث بعض البايتات بداخله. نقوم بتحديث العنصر الأخير إلى -1 (0xfffffffffffffffffffffffffffff) ، ثم قم بمسح جميع بايت هذا العنصر واحد تلو الآخر.
نسخة الكود كما يلي:
النهائي الطويل [] ar = جديد طويل [1000] ؛
الفهرس int النهائي = ar.length - 1 ؛
AR [الفهرس] = -1 ؛
system.out.println ("قبل التغيير =" + long.toHexString (ar [index])) ؛
لـ (long i = 0 ؛ i <8 ؛ ++ i)
{
Unsafe.putbyte (AR ، LongArrayoffset + 8l * index + i ، (byte) 0) ؛
System.out.println ("بعد التغيير: i =" + i + "، val =" + long.toHexString (ar [index])) ؛
}
إذا كنت ترغب في تشغيل المثال أعلاه ، فيجب عليك إضافة كتلة التعليمات البرمجية الثابتة التالية إلى فئة الاختبار الخاصة بك:
نسخة الكود كما يلي:
نهائي ثابت غير آمن غير آمن.
ثابت
{
يحاول
{
حقل الحقل = unsafe.class.getDeclaredField ("TheUnsafe") ؛
Field.SetAccessible (صحيح) ؛
غير آمن = (غير آمن) field.get (null) ؛
}
الصيد (استثناء ه)
{
رمي new RunTimeException (e) ؛
}
}
private Static Final LongarRayoffset = unfafe.arrayBaseOffset (long []. class) ؛
نتيجة الإخراج هي:
نسخة الكود كما يلي:
قبل التغيير = ffffffffffffffff
بعد التغيير: i = 0 ، val = ffffffffffff00
بعد التغيير: i = 1 ، val = fffffffffff0000
بعد التغيير: i = 2 ، val = ffffffffff0000
بعد التغيير: i = 3 ، val = ffffffff000000
بعد التغيير: i = 4 ، val = ffffff00000000000
بعد التغيير: i = 5 ، Val = FFFF0000000000000
بعد التغيير: i = 6 ، val = FF000000000000000
بعد التغيير: i = 7 ، val = 0
3. تخصيص الذاكرة من Sun.Misc.unsafe
كما ذكر أعلاه ، في جافا النقية ، يكون حجم الذاكرة الذي يمكننا تخصيصه محدودًا. تم تعيين هذا التقييد في الإصدار الأولي من Java ، وفي ذلك الوقت لم يجرؤ الناس على مشاركة عدة G من الذاكرة مثل هذا. ولكن الآن هو عصر البيانات الكبيرة ، ونحن بحاجة إلى مزيد من الذاكرة. في جافا ، هناك طريقتان للحصول على مزيد من الذاكرة:
1. تخصيص العديد من القطع الصغيرة من الذاكرة ثم استخدامها منطقياً كجزء كبير من الذاكرة.
2. استخدم sun.misc.unsafe.allcatememory (طويل) لتخصيص الذاكرة.
الطريقة الأولى هي مجرد أكثر إثارة للاهتمام من منظور الخوارزميات ، لذلك دعونا نلقي نظرة على الطريقة الثانية.
Sun.Misc.Unsafe يوفر مجموعة من الطرق لتخصيص الذاكرة وإعادة التمسك بها وإطلاقها. فهي تشبه إلى حد كبير طريقة C Malloc/Free:
1. لآخر غير آمن. allocatememory (الحجم الطويل)-تخصيص قطعة من مساحة الذاكرة. قد تحتوي هذه القطع من الذاكرة على بيانات غير مرغوب فيها (لم يتم مسحها تلقائيًا). إذا فشل التخصيص ، فسيتم طرح استثناء من java.lang.outofmemoryerror. يعيد عنوان ذاكرة غير صفري (انظر الوصف أدناه).
2.UNSAFE.REALLOCATEMEMORY (العنوان الطويل ، الحجم الطويل)-REALOCATION قطعة من الذاكرة ونسخ البيانات من المخزن المؤقت للذاكرة القديم (حيث يشير العنوان إلى) كتلة الذاكرة المخصصة حديثًا. إذا كان العنوان يساوي 0 ، فإن هذه الطريقة لها نفس التأثير مثل AlcateMemory. يعيد عنوان المخزن المؤقت للذاكرة الجديد.
3.Unsafe.freememory (عنوان طويل)-خالية من المخزن المؤقت للذاكرة التي تم إنشاؤها بواسطة الطريقتين السابقتين. إذا كان العنوان 0 ، فلن تفعل شيئًا.
يجب استخدام الذاكرة المخصصة لهذه الأساليب في وضع يسمى عنوان سجل واحد: يوفر Unfafe مجموعة من الطرق التي تقبل معلمة عنوان واحد فقط (على عكس وضع التسجيل المزدوج ، فهي تتطلب كائنًا وإزاحة الإزاحة). يمكن أن تكون الذاكرة المخصصة بهذه الطريقة أكبر من ما تقوم بتكوينه في معلمات Java لـ -xmx.
ملاحظة: لا يمكن جمع الذاكرة المخصصة من قبل غير آمن. عليك أن تعامله كمورد عادي وإدارته بنفسك.
فيما يلي مثال على استخدام غير آمن.
نسخة الكود كما يلي:
حجم int النهائي = integer.max_value / 2 ؛
addr الطويل النهائي = غير آمن. allocatememory (الحجم) ؛
يحاول
{
system.out.println ("unfafe address =" + addr) ؛
لـ (int i = 0 ؛ i <size ؛ ++ i)
{
Unsafe.putbyte (addr + i ، (byte) 123) ؛
if (unfafe.getByte (addr + i)! = 123)
System.out.println ("فشل في الإزاحة =" + i) ؛
}
}
أخيراً
{
غير آمن. freememory (addr) ؛
}
كما ترون ، باستخدام sun.misc.unsafe ، يمكنك كتابة رمز الوصول إلى الذاكرة العام للغاية: بغض النظر عن نوع الذاكرة المخصصة في Java ، يمكنك قراءة وكتابة أي نوع من البيانات حسب الرغبة.