سؤال

أنا استخدم ByteBuffers و FileChannels لكتابة البيانات الثنائية إلى ملف.عند القيام بذلك مع الملفات الكبيرة أو على التوالي لملفات متعددة، أحصل على ملف OutOfMemoryError استثناء.لقد قرأت في مكان آخر أن استخدام Bytebuffers مع NIO مكسور ويجب تجنبه.هل واجه أي منكم بالفعل هذا النوع من المشاكل ووجد حلاً لحفظ كميات كبيرة من البيانات الثنائية بكفاءة في ملف بلغة جافا؟

هو خيار jvm -XX:MaxDirectMemorySize وسيلة للذهاب؟

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

المحلول

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

نصائح أخرى

تحقق من جافا المخازن المؤقتة للبايت المعينة, ، المعروف أيضًا باسم "المخازن المؤقتة المباشرة".في الأساس، تستخدم هذه الآلية نظام ترحيل الذاكرة الظاهرية لنظام التشغيل "لتعيين" المخزن المؤقت الخاص بك مباشرةً على القرص.سيدير ​​نظام التشغيل نقل البايتات من/إلى القرص والذاكرة بطريقة سحرية وبسرعة كبيرة، ولن تضطر إلى القلق بشأن تغيير خيارات جهازك الظاهري.سيسمح لك هذا أيضًا بالاستفادة من أداء NIO المحسّن مقارنة بعمليات الإدخال/الإخراج التقليدية المستندة إلى تدفق Java، دون أي اختراقات غريبة.

المصيدتان الوحيدتان اللتان يمكنني التفكير فيهما هما:

  1. في نظام 32 بت، تقتصر على ما يقل قليلاً عن 4 جيجابايت الإجمالي لكافة مخازن البايت المعينة.(هذا في الواقع الحد الأقصى لتطبيقي، وأنا الآن أعمل على معماريات 64 بت.)
  2. التنفيذ خاص بـ JVM وليس شرطًا.أستخدم Sun's JVM ولا توجد مشاكل، لكن YMMV.

كيرك بيبردين (خبير أداء جافا مشهور إلى حد ما) يشارك في موقع ويب، www.JavaPerformanceTuning.com، الذي يحتوي على المزيد من تفاصيل MBB: نصائح أداء NIO

إذا قمت بالوصول إلى الملفات في ملف أزياء عشوائية (اقرأ هنا، تخطي، اكتب هناك، ارجع للخلف) إذن لديك مشكلة؛-)

ولكن إذا كنت تكتب ملفات كبيرة فقط، فيجب عليك ذلك بجد فكر في استخدام التدفقات. java.io.FileOutputStream يمكن استخدامه مباشرة لكتابة ملف بايت بعد بايت أو ملفوف في أي دفق آخر (أي. DataOutputStream, ObjectOutputStream) لتسهيل كتابة العوامات أو ints أو السلاسل أو حتى الكائنات القابلة للتسلسل.توجد فئات مماثلة لقراءة الملفات.

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

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

يمكن أن يعتمد هذا على بائع JDK وإصداره المحدد.

يوجد خطأ في GC في بعض أجهزة Sun JVM.لن يؤدي النقص في الذاكرة المباشرة إلى تشغيل GC في الكومة الرئيسية، ولكن يتم تثبيت الذاكرة المباشرة بواسطة ByteBuffers المباشر في الكومة الرئيسية.إذا كانت الكومة الرئيسية فارغة في الغالب، فلن يتم جمع الكثير منها لفترة طويلة.

يمكن أن يؤدي ذلك إلى حرقك حتى إذا كنت لا تستخدم المخازن المؤقتة المباشرة بنفسك، لأن JVM قد يقوم بإنشاء مخازن مؤقتة مباشرة نيابة عنك.على سبيل المثال، تؤدي كتابة ByteBuffer غير المباشرة إلى قناة المقبس إلى إنشاء مخزن مؤقت مباشر أسفل الأغطية لاستخدامه في عملية الإدخال/الإخراج الفعلية.

الحل البديل هو استخدام عدد صغير من المخازن المؤقتة المباشرة بنفسك، والاحتفاظ بها لإعادة استخدامها.

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

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