سؤال

أواجه مشكلة غريبة حقا مع تطبيق جافا.

أساسا هو صفحة ويب يستخدم ماغنوليا (نظام كمس) ، وهناك 4 حالات المتاحة على بيئة الإنتاج.في بعض الأحيان تذهب وحدة المعالجة المركزية إلى 100 ٪ في عملية جافا.

لذلك ، كان النهج الأول لجعل تفريغ موضوع ، والتحقق من موضوع المخالف ، ما وجدت كان غريبا:

"GC task thread#0 (ParallelGC)" prio=10 tid=0x000000000ce37800 nid=0x7dcb runnable 
"GC task thread#1 (ParallelGC)" prio=10 tid=0x000000000ce39000 nid=0x7dcc runnable 

حسنا ، هذا هو غريب جدا ، لم يكن لدي مشكلة مع جامع القمامة من هذا القبيل ، وبالتالي فإن الشيء التالي الذي فعلناه هو تفعيل جمكس واستخدام جفيسوالفم فحص الجهاز:كان استخدام ذاكرة الكومة مرتفعا حقا (95٪).

نهج ساذج:زيادة الذاكرة ، وبالتالي فإن المشكلة تأخذ المزيد من الوقت لتظهر ، والنتيجة ، على الخادم إعادة تشغيل مع زيادة الذاكرة (6 غيغابايت!) ظهرت المشكلة بعد 20 ساعة من إعادة التشغيل أثناء وجودها على خوادم أخرى ذات ذاكرة أقل (4 جيجابايت!) التي كانت تعمل لمدة 10 أيام ، استغرقت المشكلة بضعة أيام أخرى للظهور مرة أخرى.أيضا ، حاولت استخدام سجل أباتشي أسيس من فشل الخادم واستخدام جميتر لإعادة الطلبات إلى ملقم محلي في محاولة لإعادة إنتاج الخطأ...لم ينجح أيضا.

ثم قمت بالتحقيق في السجلات أكثر قليلا للعثور على هذه الأخطاء

info.magnolia.module.data.importer.ImportException: Error while importing with handler [brightcoveplaylist]:GC overhead limit exceeded
at info.magnolia.module.data.importer.ImportHandler.execute(ImportHandler.java:464)
at info.magnolia.module.data.commands.ImportCommand.execute(ImportCommand.java:83)
at info.magnolia.commands.MgnlCommand.executePooledOrSynchronized(MgnlCommand.java:174)
at info.magnolia.commands.MgnlCommand.execute(MgnlCommand.java:161)
at info.magnolia.module.scheduler.CommandJob.execute(CommandJob.java:91)
at org.quartz.core.JobRunShell.run(JobRunShell.java:216)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549)
    Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded

مثال آخر

    Caused by: java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.util.Arrays.copyOf(Arrays.java:2894)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:117)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:407)
    at java.lang.StringBuilder.append(StringBuilder.java:136)
    at java.lang.StackTraceElement.toString(StackTraceElement.java:175)
    at java.lang.String.valueOf(String.java:2838)
    at java.lang.StringBuilder.append(StringBuilder.java:132)
    at java.lang.Throwable.printStackTrace(Throwable.java:529)
    at org.apache.log4j.DefaultThrowableRenderer.render(DefaultThrowableRenderer.java:60)
    at org.apache.log4j.spi.ThrowableInformation.getThrowableStrRep(ThrowableInformation.java:87)
    at org.apache.log4j.spi.LoggingEvent.getThrowableStrRep(LoggingEvent.java:413)
    at org.apache.log4j.AsyncAppender.append(AsyncAppender.java:162)
    at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
    at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
    at org.apache.log4j.Category.callAppenders(Category.java:206)
    at org.apache.log4j.Category.forcedLog(Category.java:391)
    at org.apache.log4j.Category.log(Category.java:856)
    at org.slf4j.impl.Log4jLoggerAdapter.error(Log4jLoggerAdapter.java:576)
    at info.magnolia.module.templatingkit.functions.STKTemplatingFunctions.getReferencedContent(STKTemplatingFunctions.java:417)
    at info.magnolia.module.templatingkit.templates.components.InternalLinkModel.getLinkNode(InternalLinkModel.java:90)
    at info.magnolia.module.templatingkit.templates.components.InternalLinkModel.getLink(InternalLinkModel.java:66)
    at sun.reflect.GeneratedMethodAccessor174.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:622)
    at freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:866)
    at freemarker.ext.beans.BeanModel.invokeThroughDescriptor(BeanModel.java:277)
    at freemarker.ext.beans.BeanModel.get(BeanModel.java:184)
    at freemarker.core.Dot._getAsTemplateModel(Dot.java:76)
    at freemarker.core.Expression.getAsTemplateModel(Expression.java:89)
    at freemarker.core.BuiltIn$existsBI._getAsTemplateModel(BuiltIn.java:709)
    at freemarker.core.BuiltIn$existsBI.isTrue(BuiltIn.java:720)
    at freemarker.core.OrExpression.isTrue(OrExpression.java:68)

ثم اكتشفت ذلك هذه المشكلة ترجع إلى جامع القمامة باستخدام طن من وحدة المعالجة المركزية ولكن غير قادر على تحرير الكثير من الذاكرة

حسنا ، لذلك هو مشكلة مع الذاكرة التي تتجلى في وحدة المعالجة المركزية ، لذلك إذا تم حل مشكلة استخدام الذاكرة ، ثم وحدة المعالجة المركزية يجب أن يكون على ما يرام ، لذلك أخذت هيبدومب ، لسوء الحظ كان مجرد كبيرة جدا لفتحه (كان الملف 10 جيجابايت) ، على أي حال أنا تشغيل الخادم لوكاليم تحميلها قليلا وأخذ هيبدومب ، بعد فتحه ، وجدت شيئا مثيرا للاهتمام:

هناك طن من حالات

AbstractReferenceMap$WeakRef  ==> Takes 21.6% of the memory, 9 million instances
AbstractReferenceMap$ReferenceEntry  ==> Takes 9.6% of the memory, 3 million instances

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

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

المشاكل بالنسبة لي هي

  1. لا يمكنني إعادة إنتاج الخطأ
  2. لا أستطيع معرفة أين الجحيم الذاكرة يتسرب (إذا كان هذا هو الحال)

أي أفكار على الإطلاق?

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

المحلول

'لا المرجع' finalize() يجب بالتأكيد إزالة الأساليب لأنها من المرجح أن تجعل أي مشاكل أداء غ أسوأ.ولكن أظن أن لديك مشاكل تسرب الذاكرة الأخرى كذلك.

نصائح:

  • أولا التخلص من عديمة الفائدة finalize() طرق.

  • إذا كان لديك أخرى finalize() طرق ، والنظر في التخلص منها.(اعتمادا على الانتهاء من القيام بالأشياء بشكل عام فكرة سيئة ...)

  • استخدم ملف تعريف الذاكرة لمحاولة تحديد الكائنات التي يتم تسريبها ، وما الذي يسبب التسرب.هناك الكثير من الأسئلة لذلك ...وغيرها من الموارد على إيجاد التسريبات في كود جافا.على سبيل المثال:


الآن إلى الأعراض الخاصة بك.

أولا وقبل كل شيء ، المكان الذي يوجد فيه OutOfMemoryErrorألقيت الصورة هو على الارجح غير ذي صلة.

ومع ذلك ، فإن حقيقة أن لديك أعدادا هائلة من AbstractReferenceMap$WeakRef و AbstractReferenceMap$ReferenceEntry الكائنات هي مؤشر سلسلة على أن شيئا ما في التطبيق الخاص بك أو المكتبات التي يستخدمها يقوم بكمية كبيرة من التخزين المؤقت ...وأن هذا التخزين المؤقت متورط في المشكلة.(ال AbstractReferenceMap الطبقة هي جزء من مكتبة مجموعات أباتشي كومنز.إنها الطبقة الفائقة من ReferenceMap و ReferenceIdentityMap.)

تحتاج إلى تعقب كائن الخريطة (أو الكائنات) التي تلك WeakRef و ReferenceEntry الكائنات تنتمي إلى ، والكائنات (الهدف) التي تشير إليها.ثم تحتاج إلى معرفة ما هو إنشائه / لهم ومعرفة لماذا لا يتم مسح الإدخالات استجابة للطلب الذاكرة عالية.

  • هل لديك إشارات قوية إلى الكائنات المستهدفة في مكان آخر (والتي من شأنها أن توقف ويكريفس من كسرها)?

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

  • والخرائط المستخدمة من قبل المواضيع متعددة دون التزامن الخارجي?يمكن أن يؤدي ذلك إلى الفساد ، والذي يمكن أن يظهر على أنه تسرب تخزين هائل.


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


وأخيرا ، ملاحظتك أن المشكلة هي أسوأ عندما كومة أكبر.بالنسبة لي ، هذا لا يزال متسقا مع Reference / مشكلة متعلقة بذاكرة التخزين المؤقت.

  • Reference الكائنات هي أكثر عمل ل غ من المراجع العادية.

  • عندما يحتاج غ إلى " كسر " أ Reference, ، هذا يخلق المزيد من العمل;على سبيل المثال.معالجة قوائم الانتظار المرجعية.

  • حتى عندما يحدث ذلك ، لا يزال لا يمكن جمع الكائنات التي لا يمكن الوصول إليها حتى دورة غ القادمة في أقرب وقت ممكن.

حتى أستطيع أن أرى كيف كومة 6 غيغابايت كاملة من المراجع من شأنه أن يزيد بشكل كبير من النسبة المئوية للوقت الذي يقضيه في غ ...بالمقارنة مع كومة 4 جيجابايت ، والتي يمكن أن تسبب آلية "غ الحد العلوي" لركلة في وقت سابق.

لكنني أعتقد أن هذا عرض عرضي وليس السبب الجذري.

نصائح أخرى

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

بعد أن تتمكن من إعادة إنتاجه ، حاول تقليل حجم الكومة لمعرفة ما إذا كان يمكنك إعادة إنتاجه بشكل أسرع.ولكن القيام بذلك الثانية منذ كومة صغيرة قد لا تصل إلى" الحد النفقات العامة غ " مما يعني أن غ تنفق الوقت المفرط (98 ٪ من قبل بعض التدبير) في محاولة لاستعادة الذاكرة.

لتسرب الذاكرة ، تحتاج إلى معرفة أين في التعليمات البرمجية انها تتراكم المراجع إلى الكائنات.على سبيل المثال.هل بناء خريطة لجميع طلبات الشبكة الواردة?بحث على شبكة الإنترنت https://www.google.com/search?q=how+to+debug+java+memory+leaks يعرض العديد من المقالات المفيدة حول كيفية تصحيح تسرب ذاكرة جافا ، بما في ذلك نصائح حول استخدام أدوات مثل محلل ذاكرة الكسوف الذي تستخدمه.بحث عن رسالة الخطأ المحددة https://www.google.com/search?q=GC+overhead+limit+exceeded مفيد أيضا.

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

لذلك بمجرد أن تتمكن من إعادة إنتاج المشكلة ، حاول حذف تلك عدم التشغيل finalize() الطرق ومعرفة ما إذا كانت المشكلة تستغرق وقتا أطول للتكاثر.

من المهم أن هناك الكثير AbstractReferenceMap$WeakRef مثيلات في الذاكرة.نقطة أ إشارة ضعيفة هو الإشارة إلى كائن دون إجباره على البقاء في الذاكرة. خلاصة المرجعالخريطة هي خريطة تتيح للمرء أن يجعل المفاتيح و / أو القيم مراجع ضعيفة أو مراجع ناعمة.(نقطة أ مرجع لينة هو محاولة للحفاظ على كائن في الذاكرة ولكن السماح غ تحريره عندما يحصل الذاكرة منخفضة.) على أي حال ، من المحتمل أن تؤدي جميع حالات الضعف في الذاكرة إلى تفاقم المشكلة ولكن لا ينبغي الاحتفاظ بمفاتيح/قيم الخريطة المشار إليها في الذاكرة.ما هم في اشارة الى?ماذا يشير إلى تلك الأشياء?

جرب أداة تقوم بتوقيف التسريبات في التعليمات البرمجية المصدرية مثل Plumbr

هناك عدد من الاحتمالات، وربما بعضها استكشافتها.

بالتأكيد تسرب ذاكرة من نوع ما.

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

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

يجب أن تحاول إجراء اختبار تحميل على خادم غير إنتاج حيث تحاكي الاستخدام العادي لتطبيقك بأعداد كبيرة من المستخدمين. ربما حتى الحد حتى من ذاكرة الخادم أقل من المعتاد.

الحظ السعيد، مشاكل الذاكرة هي ألم لتتبع.

أنت تقول أنك قد حاولت بالفعل Jvisualvm، لفحص الجهاز.ربما، جربها مرة أخرى، مثل هذا:

  • هذه المرة إلقاء نظرة على علامة التبويب "Sampler -> الذاكرة".

  • يجب أن أقول لك أي كائنات (أنواع) كائنات تشغل معظم الذاكرة.

  • ثم اكتشف أين يتم إنشاء مثل هذه الكائنات وإزالتها.

  • يمكن أن يكون سبب أخطاء "غريبة" الكثير من الأوقات التي توصلها وكلاء Java إلى JVM. إذا كان لديك أي وكلاء يعملون (على سبيل المثال Jrebel / Liverebel، Newrelic، JProfiler)، فحاول تشغيل بدونهم أولا.
  • أشياء غريبة يمكن أن تحدث أيضا عند تشغيل JVM مع المعلمات غير القياسية (-xx)؛ من المعروف أن بعض المجموعات تسبب مشاكل؛ ما المعلمات التي تستخدمها حاليا؟
  • تسرب الذاكرة يمكن أن يكون أيضا في ماغنوليا نفسها، هل حاولت googling "تسرب ماغنوليا"؟ هل تستخدم أي وحدات Magnolia ذات الحفلات الثالث؟ إذا كان ذلك ممكنا، حاول تعطيل / إزالةها.

قد تكون المشكلة متصلا بجزء واحد فقط من يمكنك محاولة إعادة إنتاج المشكلة عن طريق "إعادة تشغيل" سجلات الوصول الخاصة بك على خادم التدريج / التطوير الخاص بك.

إذا كان أي شيء آخر يعمل، إذا كان الأمر كذلك، فسأفعل ما يلي: - محاولة تكرار المشكلة في مثيل ماغنوليا "فارغ" (دون أي من التعليمات البرمجية الخاصة بي) - محاولة تكرار المشكلة في مثيل ماغنوليا "فارغ" (بدون وحدات الطرف الثالث) - محاولة ترقية جميع البرامج (ماغنوليا، وحدات الطرف الثالث، JVM) - حاول أخيرا تشغيل موقع الإنتاج مع Yourkit وحاول العثور على التسرب

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

hth، جان

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

من الجيد أيضا تعيين مثيل كائن إلى null عندما تحتاج إلى إزالته / تنظيفه.هذا لأن finalize() الطريقة سطحية وشريرة ، وأحيانا لن يتم استدعاؤها من قبل جامع القمامة.أفضل حل لهذا هو أن نسميها (أو طريقة أخرى مماثلة) بنفسك.بهذه الطريقة ، أنت متأكد من أنه تم إجراء تنظيف القمامة بنجاح.كما قال جوشوا بلوخ في كتابه:جافا فعالة, 2الطبعة الثانية, بند 7, صفحة 27:تجنب النهائيات."النهائيون لا يمكن التنبؤ بهم ، وغالبا ما يكونون خطرين وغير ضروريين بشكل عام".يمكنك رؤية القسم هنا.

نظرا لعدم وجود رمز معروض ، لا يمكنني معرفة ما إذا كانت أي من هذه الطرق يمكن أن تكون مفيدة ، ولكن لا يزال من المفيد معرفة هذه الأشياء.نأمل أن تساعدك هذه النصائح!

كما يوصى أعلاه، أود أن أتواصل مع ديفس ماغنوليا، ولكن في الوقت نفسه:

أنت تحصل على هذا الخطأ لأن GC لا يجمع كثيرا على تشغيل

جامع المتزامن سوف يرمي OutofMemoryError إذا كثير جدا يتم إنفاق الوقت في مجموعة القمامة: إذا كان أكثر من 98٪ من يتم إنفاق إجمالي الوقت في جمع القمامة وأقل من 2٪ من كومة يتم استردادها، وسيتم إلقاء outofmemoryError.

نظرا لأنك لا تستطيع تغيير التنفيذ، فسوف أوصي بتغيير تكوين GC، بطريقة تعمل بشكل أقل تواترا، لذلك سيكون من المرجح أن تفشل بهذه الطريقة.

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

مخارق VM الخاصة بي هي كما تتبع: -xms= 6G. -xmx= 6G. -xx: maxpermsize= 1G -xx: Newsize= 2G -xx: maxtenuringthreshold= 8 -xx: survivorratio= 7 -xx: + useconcmarksweepgc -xx: + cmsclassunloadingenabled -xx: + cmspermgensweepingenabled -xx: cmsinitiatingoccocityfract= 60 -xx: + heapdumponoutofmemoryError -xx: + printgcdetails -xx: + printgctimestamps -xx: + printtenurationDistribution -xloggc: سجلات / gc.log

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