سؤال

هدفي هو التأكد من تخصيص مجموعة مخصصة في Java عبر الذاكرة المادية المتجاورة. المشكلة التي أواجهتها هي أن الصفحات التي يتم تخصيص صفيف عبرها لا تميل إلى أن تكون متجاورة في الذاكرة المادية ، إلا إذا قمت بتخصيص صفيف كبير حقًا.

أسئلتي هي:

  • لماذا تضمن صفيف كبير حقًا الصفحات المتجاورة في الذاكرة الفعلية؟
  • هل هناك أي طريقة لضمان تخصيص صفيف عبر الذاكرة المادية ، وهذا لا ينطوي على جعل الصفيف كبيرًا حقًا؟
  • كيف يمكنني معرفة ما هي الصفحة أو العنوان المادي الذي يوجد فيه كائن/صفيف Java ، دون قياس ضربات ذاكرة التخزين المؤقت/ذاكرة التخزين المؤقت؟

أنا لا أبحث عن إجابات تسأل لماذا أفعل هذا في جافا. أنا أفهم أن C "سيحل مشكلتي" ، وأنني أتعارض مع الطبيعة الأساسية لجافا. ومع ذلك لدي سبب وجيه للقيام بذلك.

لا يلزم ضمان الإجابات للعمل طوال الوقت. أبحث عن إجابات تعمل معظم الوقت. نقاط إضافية للإجابات الإبداعية الخارجة عن الصندوق التي لن يكتبها أي مبرمج Java المعقول على الإطلاق. لا بأس أن تكون منصة محددة (x86 32 بت 64 بت).

هل كانت مفيدة؟

المحلول

بالنظر إلى أن جامع القمامة ينقل الأشياء في الذاكرة (المنطقية) ، أعتقد أنك ستكون محظوظًا.

حول أفضل ما يمكنك فعله هو استخدام bytebuffer.allocatedirect. لن يتم نقل ذلك (عادةً) ذاكرة (منطقية) بواسطة GC ، ولكن قد يتم نقلها في الذاكرة الفعلية أو حتى تتجول إلى القرص. إذا كنت تريد أي ضمانات أفضل ، فسيتعين عليك الوصول إلى نظام التشغيل.

بعد قولي هذا ، إذا تمكنت من تعيين حجم الصفحة ليكون كبيرًا مثل كومةك ، فستكون جميع المصفوفات بالضرورة متجاورة جسديًا (أو تم تبديلها).

نصائح أخرى

لا. تتطلب الذاكرة المتجاورة جسديًا تفاعلًا مباشرًا مع نظام التشغيل. معظم التطبيقات ، تضمنت JVM فقط الحصول على عناوين متجاورة تقريبًا. ولا يمكن لـ JVM أن يعطيك ما لا يحصل عليه من نظام التشغيل.

الى جانب ذلك ، لماذا تريد ذلك؟ إذا كنت تقوم بإعداد عمليات نقل DMA ، فربما تستخدم تقنيات إلى جانب Java على أي حال.

جزء من الخلفية:

عادةً ما تكون الذاكرة الفعلية في جهاز كمبيوتر حديثًا كمية مرنة ، على وحدات DIMM القابلة للاستبدال. يحتوي كل بايت منه على عنوان فعلي ، وبالتالي يحدد نظام التشغيل أثناء التمهيد العناوين المادية المتوفرة. اتضح أن التطبيقات أفضل من خلال عدم استخدام هذه العناوين مباشرة. بدلاً من ذلك ، تستخدم جميع وحدات المعالجة المركزية الحديثة (واختداءاتها المخبأة) عناوين افتراضية. يوجد جدول رسم خرائط للعناوين المادية ، ولكن لا يلزم إكمالًا - يتم تمكين المبادلة إلى القرص عن طريق استخدام العناوين الافتراضية التي لا يتم تعيينها للعناوين المادية. يتم الحصول على مستوى آخر من المرونة من وجود جدول واحد لكل عملية ، مع تعيينات غير مكتملة. إذا كان للعملية A عنوان افتراضي يقوم بتعيينه على العنوان الفعلي X ، ولكن العملية B لا ، فلا توجد طريقة يمكن للعملية B الكتابة إلى العنوان الفعلي X ، ويمكننا أن نعتبر أن الذاكرة حصرية للعملية A. لكي يكون هذا آمنًا ، يتعين على نظام التشغيل حماية الوصول إلى طاولة رسم الخرائط ، ولكن جميع الأنفاد الحديثة تفعل ذلك.

يعمل جدول التعيين على مستوى الصفحة. يتم تعيين صفحة ، أو مجموعة فرعية متجاورة من العناوين المادية إلى مجموعة فرعية متجاورة من العناوين الافتراضية. أدت المفاضلة بين النفقات العامة والتحسينات إلى أن تكون صفحات 4 كيلو بايت بحجم صفحة شائع. ولكن نظرًا لأن كل صفحة لها رسم خرائط خاص بها ، لا يمكن للمرء أن يفترض التواصل بعد حجم الصفحة. على وجه الخصوص ، عندما يتم إخلاء الصفحات من الذاكرة المادية ، وتم تبديلها على القرص ، واستعادتها ، فمن الممكن تمامًا أن ينتهي الأمر على عنوان ذاكرة فعلية جديدة. لا يلاحظ البرنامج ، لأن العنوان الافتراضي لا يتغير ، فقط جدول رسم الخرائط الذي يديره نظام التشغيل.

أعتقد أنك تريد استخدامها sun.java.unsafe.

قد تكون هناك طرق لخداع JVM معين للقيام بما تريد ، ولكن من المحتمل أن تكون هذه هشة ومعقدة ومن المرجح أن تكون محددة للغاية لـ JVM ، ونسخةها ، ونظام التشغيل الذي يعمل عليه وما إلى ذلك ، وبعبارة أخرى ، جهد ضائع.

لذلك دون معرفة المزيد عن مشكلتك ، لا أعتقد أن أي شخص سيتمكن من المساعدة. بالتأكيد لا توجد طريقة للقيام بذلك في جافا بشكل عام ، على الأكثر على JVM محددة.

لاقتراح بديل:

إذا كنت بحاجة حقًا إلى تخزين البيانات في الذاكرة المتجاورة ، فلماذا لا تفعل ذلك في مكتبة C صغيرة والاتصال بها عبر JNI؟

كما اراه. لا يزال عليك أن تشرح السبب بعد

  • أن المصفوفات البدائية ليست مستمرة في الذاكرة. لا أرى لماذا لن يكونوا مستمرين في الذاكرة الافتراضية. (من غير المرجح أن تكون صفائف الكائنات من الكائنات مستمرة في الذاكرة)
  • الصفيف غير المستمر في الذاكرة الفعلية (ذاكرة الوصول العشوائي) سيكون لها فرق كبير في الأداء. على سبيل المثال الفرق القابل للقياس في أداء التطبيق الخاص بك.

ما يبدو عليه هو أنك تبحث حقًا عن طريقة منخفضة المستوى لتخصيص المصفوفات لأنك معتاد على القيام بذلك في C ، والأداء هو مطالبة بالحاجة إلى القيام بذلك.

راجع للشغل: يمكن أن يكون الوصول إلى bytebuffer.allocatedirect () مع getDouble ()/putdouble () أبطأ فقط باستخدام مضاعفة [] لأن الأول يتضمن مكالمات JNI ويمكن تحسين الأخير إلى عدم مكالمة على الإطلاق.

السبب في استخدامه هو تبادل البيانات بين مساحات Java و C. مثل مكالمات NIO. يعمل بشكل جيد فقط عند الاحتفاظ بقراءة/كتابة إلى الحد الأدنى. وإلا فإنك أفضل حالًا في استخدام شيء ما في مساحة Java.

أي ما لم تكن واضحًا ما تفعله و لماذا أنت تفعل ذلك ، يمكنك أن ينتهي بك الأمر بحل قد يجعلك تشعر بتحسن ، ولكنه في الواقع أكثر تعقيدًا وأداء أسوأ من الحل البسيط.

ملحوظة هذا الجواب إلى سؤال ذي صلة ، والذي يناقش system.identityHashCode () وتحديد عنوان ذاكرة الكائن. خلاصة القول هي أنه يمكنك استخدام تطبيق HashCode () Array Array () لتحديد عنوان الذاكرة الأصلي للمصفوفة (مع مراعاة التركيب في INT/32 بت)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top