การจัดการกับข้อผิดพลาด“ java.lang.OutOfMemoryError: พื้นที่ PermGen”


1222

เมื่อเร็ว ๆ นี้ฉันพบข้อผิดพลาดในเว็บแอปพลิเคชันของฉัน:

java.lang.OutOfMemoryError: พื้นที่ PermGen

มันเป็นแอปพลิเคชั่น Hibernate / JPA + IceFaces / JSF ที่ทำงานบน Tomcat 6 และ JDK 1.6 เห็นได้ชัดว่าสิ่งนี้อาจเกิดขึ้นได้หลังจากปรับใช้แอปพลิเคชันอีกครั้งสองสามครั้ง

อะไรเป็นสาเหตุและสิ่งที่สามารถทำได้เพื่อหลีกเลี่ยง ฉันจะแก้ไขปัญหาได้อย่างไร


ฉันต่อสู้มาหลายชั่วโมงแล้ว แต่ก็ไม่มีข่าวดี ดูคำถามที่เกี่ยวข้องของฉัน: stackoverflow.com/questions/1996088/…คุณอาจยังมีการรั่วไหลของหน่วยความจำเช่นคลาสจะไม่ถูกเก็บรวบรวมขยะเนื่องจาก WebAppClassLoader ของคุณไม่ได้เก็บรวบรวมขยะ (มีการอ้างอิงภายนอกที่ไม่ได้ล้าง) การเพิ่ม PermGen จะหน่วงเวลา OutOfMemoryError เท่านั้นและการอนุญาตให้มีการรวบรวมขยะคลาสเป็นสิ่งที่จำเป็น แต่จะไม่เก็บขยะคลาสหากคลาสตัวโหลดคลาสของพวกเขายังมีการอ้างอิงอยู่
Eran Medan

ผมได้รับข้อผิดพลาดนี้ที่เพิ่มtaglib จอแสดงผล การลบจึงแก้ไขข้อผิดพลาดได้เช่นกัน ทำไมเป็นเช่นนั้น
masT

และคุณพบมันได้อย่างไร?
Thorbjørn Ravn Andersen

13
ใช้ JDK 1.8: þยินดีต้อนรับสู่ MetaSpace
Rytek

หากใช้ Windows ให้ทำตามคำแนะนำเหล่านี้แทนที่จะพยายามตั้งค่าสถานะด้วยตนเองในไฟล์ปรับแต่ง ซึ่งเป็นการตั้งค่าในรีจิสทรีที่จะเรียกใช้โดย Tomcat ในระหว่างรันไทม์ stackoverflow.com/questions/21104340/…
MacGyver

คำตอบ:


563

วิธีแก้ไขคือเพิ่มแฟล็กเหล่านี้ในบรรทัดรับคำสั่ง JVM เมื่อ Tomcat เริ่มทำงาน:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

คุณสามารถทำได้โดยปิดบริการ tomcat จากนั้นไปที่ไดเรกทอรี Tomcat / bin และเรียกใช้ tomcat6w.exe ภายใต้แท็บ "Java" ให้เพิ่มอาร์กิวเมนต์ลงในกล่อง "Java Options" คลิก "ตกลง" จากนั้นเริ่มบริการใหม่

หากคุณได้รับข้อผิดพลาดบริการที่ระบุไม่มีอยู่ในฐานะบริการที่ติดตั้งคุณควรรัน

tomcat6w //ES//servicename

โดยที่servicenameเป็นชื่อของเซิร์ฟเวอร์ตามที่ดูใน services.msc

ที่มา: ความคิดเห็น orx เกี่ยวกับเอริคตอบเปรียว


39
บทความด้านล่างแนะนำ -XX: + UseConcMarkSweepGC และ -XX: MaxPermSize = 128m เช่นกัน my.opera.com/karmazilla/blog/2007/03/13/…
Taylor Leese

30
-XX: + CMSPermGenSweepingEnabled ตัวเลือกนี้จะทำให้ประสิทธิภาพลดลง ทำให้แต่ละคำขอใช้เวลามากกว่าระบบของเราสามเท่า ใช้ด้วยความระมัดระวัง
Eldelshell

10
ทำงานให้ฉัน - ขอบคุณ - ฉันกำลังทำสิ่งนี้บน Ubuntu 10.10 กับ Tomcat6 - ฉันสร้างไฟล์ใหม่: /usr/share/tomcat6/bin/setenv.sh และเพิ่มบรรทัดต่อไปนี้: JAVA_OPTS = "- Xms256m -Xmx512m - XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled" - เริ่มต้นใหม่คราวโดยใช้: sudo /etc/init.d/tomcat6 เริ่มต้น
sami

24
ในการเริ่มต้น Tomcat 6.0.29 จากไฟล์บันทึก catalina.out ของฉัน: "โปรดใช้ CMSClassUnloadingEnabled แทน CMSPermGenSweepingEnabled ในอนาคต"
knb

222
ก่อนอื่นจะเป็นการดีที่จะอธิบายว่าธงเหล่านี้ทำอะไรได้ดี เพียงแค่พูดว่า: "ทำอย่างนั้นและเพลิดเพลินไปกับ" ไม่เพียงพอ IMHO
Nikem

251

คุณควรพยายามมากกว่า-XX:MaxPermSize=128M-XX:MaxPermGen=128M

ฉันไม่สามารถบอกการใช้งานพูลหน่วยความจำนี้ได้อย่างแม่นยำ แต่ต้องทำกับจำนวนคลาสที่โหลดลงใน JVM (ดังนั้นการเปิดใช้งานการยกเลิกการโหลดคลาสสำหรับ Tomcat สามารถแก้ไขปัญหาได้) หากแอปพลิเคชันของคุณสร้างและคอมไพล์คลาสที่รันจะมีแนวโน้มที่จะต้องมีพูลหน่วยความจำขนาดใหญ่กว่าค่าเริ่มต้น


9
ที่จริงแล้วจะเป็นการเลื่อน OOMError เท่านั้น ดูคำตอบด้านล่างโดย anon โดยมีลิงก์ไปยังบล็อกของ frankkieviet
Rade_303

ตัวเลือกเหล่านี้จะมีการอธิบายที่นี่: oracle.com/technetwork/java/javase/tech/...
amos

153

ข้อผิดพลาด PermGen ของเซิร์ฟเวอร์แอปที่เกิดขึ้นหลังจากการปรับใช้หลายครั้งมักเกิดจากการอ้างอิงที่ถือโดยคอนเทนเนอร์ไปยัง classloaders ของแอปเก่าของคุณ ตัวอย่างเช่นการใช้คลาสระดับบันทึกที่กำหนดเองจะทำให้การอ้างอิงถูกเก็บไว้โดย classloader ของเซิร์ฟเวอร์แอป คุณสามารถตรวจสอบการรั่วไหลของ class-classloader เหล่านี้โดยใช้เครื่องมือการวิเคราะห์ JVM สมัยใหม่ (JDK6 +) เช่น jmap และ jhat เพื่อดูว่าคลาสใดที่ยังคงมีคลาสอยู่ในแอปของคุณและออกแบบหรือกำจัดการใช้งาน ผู้ต้องสงสัยตามปกติคือฐานข้อมูลตัวบันทึกและไลบรารีฐานระดับกรอบงานอื่น ๆ

ดูการรั่วไหลของ classLoader: หวั่น "java.lang.OutOfMemoryError: พื้นที่ PermGen" ข้อยกเว้นและโดยเฉพาะอย่างยิ่งของการโพสต์ติดตาม


3
นี่เป็นวิธีการแก้ปัญหาที่แท้จริงซึ่งสามารถทำได้ในบางกรณียากที่จะนำไปใช้
Rade_303

แหล่งข้อมูลที่ดีอีกอย่างคือpeople.apache.org/~markt/presentations/… (จาก Tomcat release manager !!)
gavenkoa

22
ในขณะที่สิ่งนี้อาจตอบคำถามในทางทฤษฎีมันก็ควรที่จะรวมส่วนสำคัญของคำตอบที่นี่และให้ลิงค์สำหรับการอ้างอิง
Joachim Sauer

68

ความผิดพลาดทั่วไปที่ผู้คนทำกันคือการคิดว่าฮีปสเปซและพื้นที่เพอร์เกนนั้นเหมือนกัน คุณสามารถมีพื้นที่เหลืออยู่ในกองมาก แต่ก็ยังสามารถมีหน่วยความจำไม่เพียงพอใน Permgen

สาเหตุทั่วไปของ OutofMemory ใน PermGen คือ ClassLoader เมื่อใดก็ตามที่มีการโหลดคลาสลงใน JVM ข้อมูลเมตาทั้งหมดพร้อมกับ Classloader จะถูกเก็บไว้ในพื้นที่ PermGen และจะถูกเก็บรวบรวมขยะเมื่อ Classloader ที่โหลดพวกเขาพร้อมสำหรับการรวบรวมขยะ ใน Case Classloader มีหน่วยความจำรั่วกว่าคลาสที่โหลดทั้งหมดจะยังคงอยู่ในหน่วยความจำและทำให้ PermGen outofmemory เมื่อคุณทำซ้ำสองครั้ง ตัวอย่างคลาสสิกเป็นjava.lang.OutOfMemoryError: PermGen อวกาศใน Tomcat

ขณะนี้มีสองวิธีในการแก้ไขปัญหานี้:
1. ค้นหาสาเหตุของ Memory Leak หรือหากมีหน่วยความจำรั่ว
ขนาด 2. การเพิ่มขึ้นของ PermGen อวกาศโดยใช้ JVM พระรามและ-XX:MaxPermSize-XX:PermSize

คุณสามารถตรวจสอบ2 Solution ของ Java.lang.OutOfMemoryErrorใน Java สำหรับรายละเอียดเพิ่มเติม


3
วิธีการผ่านพารามิเตอร์-XX:MaxPermSize and -XX:PermSize? catalina.batฉันไม่สามารถหา 5.5.26รุ่นคราวของฉันคือ
Deckard

วิธีค้นหาหน่วยความจำรั่วของคลาสโหลดเดอร์? คุณแนะนำเครื่องมือใด ๆ
Amit

@amit สำหรับคำแนะนำเครื่องมือโปรดดูคำตอบของวิกิชุมชนในคำถามนี้
Barett

@Deckard ไปยังไดเรกทอรี Tomcat / bin และเรียกใช้ tomcat6w.exe ภายใต้แท็บ "Java" ให้เพิ่มอาร์กิวเมนต์ลงในกล่อง "Java Options" คลิก "ตกลง"
Zeb

43

ใช้พารามิเตอร์บรรทัดคำสั่ง-XX:MaxPermSize=128mสำหรับ Sun JVM (เห็นได้ชัดว่าทดแทน 128 สำหรับขนาดที่คุณต้องการ)


9
ปัญหาเพียงอย่างเดียวก็คือคุณเพิ่งจะชะลอสิ่งที่หลีกเลี่ยงไม่ได้ - ในบางจุดคุณก็จะไม่มีที่ว่างเหลืออีกด้วย มันเป็นวิธีแก้ปัญหาที่ยอดเยี่ยม แต่ก็ไม่ได้แก้ปัญหาอย่างถาวร
ทิมฮาวแลนด์

สิ่งเดียวกันนี้เกิดขึ้นใน Eclipse และทุกครั้งที่คุณมีการโหลดคลาสแบบไดนามิกจำนวนมาก ผู้จัดทำ classloaders นั้นไม่ได้ถูกกำจัดและอยู่ในยุคถาวรตลอดชั่วนิรันดร์
แมตต์

1
ฉันหมดสิทธิ์ใช้งาน PermGen เมื่อทำงานฮัดสันที่มีขนาดใหญ่เป็นพิเศษโดยเฉพาะ ...
HDave

10
@ TimHowland มันสามารถแก้ไขได้อย่างถาวรหากสาเหตุหลักไม่ใช่การรั่วไหลของ classloader เพียงคลาส / ข้อมูลคงที่มากเกินไปในเว็บแอปของคุณ
PéterTörök

มีปัญหาเช่นเดียวกับ HDave เมื่อสร้าง jenkins / hudson จากแหล่งที่มา
louisgab

38

ลอง-XX:MaxPermSize=256mและถ้ามันยังคงอยู่ลอง-XX:MaxPermSize=512m


56
และถ้ามันยังคงมีอยู่ลองXX:MaxPermSize=1024m:)
IGO

38
และถ้ามันยังคงยังคงพยายามที่ XX: MaxPermSize = 2048m :)
โทมัส

16
และถ้ายังคงมีอยู่ให้คิดใหม่ใบสมัครของคุณ !! หรือลอง XX: MaxPermSize = 4096m :)
Jonathan Airey

17
คุณสามารถลอง 8192m ได้ แต่นั่นก็เกินความจริงไปหน่อย
Jacek Pietal

12
เกินขนาดจริง ๆ - 640KB น่าจะเพียงพอสำหรับทุกคน!
Joel Purra

28

ฉันได้เพิ่ม -XX: MaxPermSize = 128m (คุณสามารถทดลองใช้งานได้ดีที่สุด) กับอาร์กิวเมนต์ VMในขณะที่ฉันใช้ eclipse ide ใน JVM ส่วนใหญ่PermSize เริ่มต้นจะอยู่ที่ประมาณ64MBซึ่งมีหน่วยความจำไม่เพียงพอหากมีคลาสมากเกินไปหรือ Strings จำนวนมากในโครงการ

สำหรับ eclipse จะมีคำอธิบายอยู่ที่คำตอบด้วย

ขั้นตอนที่ 1 : ดับเบิลคลิกที่เซิร์ฟเวอร์ tomcat ที่แท็บเซิร์ฟเวอร์

ป้อนคำอธิบายรูปภาพที่นี่

ขั้นตอนที่ 2 : การเปิดตัวเปิด Confและเพิ่ม-XX: MaxPermSize = 128mในส่วนท้ายของที่มีอยู่arguements VM

ป้อนคำอธิบายรูปภาพที่นี่


1
ขอบคุณสำหรับคำตอบที่ดีที่สุดของคุณ (หมายเหตุ: คลิก "เปิดการกำหนดค่าการเปิดตัว" เพื่อเปิด "แก้ไขการกำหนดค่า" ... แต่ฉันใช้พารามิเตอร์ต่อไปนี้: "-XX: + CMSClassUnloadingEnabled -XX: + CMSPermGenSweepingEnabled"
Chris Sim

23

ฉันได้รับการพิจารณาหัวของฉันกับปัญหานี้ในขณะที่การปรับใช้และยกเลิกการปรับใช้โปรแกรมประยุกต์บนเว็บที่ซับซ้อนด้วยและคิดว่าฉันจะเพิ่มคำอธิบายและการแก้ปัญหาของฉัน

เมื่อฉันปรับใช้แอปพลิเคชันบน Apache Tomcat ClassLoader ใหม่จะถูกสร้างขึ้นสำหรับแอปนั้น จากนั้น ClassLoader จะถูกใช้เพื่อโหลดคลาสทั้งหมดของแอปพลิเคชันและในการเลิกใช้งานทุกอย่างควรจะหายไปอย่างสวยงาม อย่างไรก็ตามในความเป็นจริงมันไม่ง่ายอย่างนั้น

คลาสอย่างน้อยหนึ่งคลาสที่สร้างขึ้นในช่วงชีวิตของเว็บแอปพลิเคชันมีการอ้างอิงแบบคงที่ซึ่งบางแห่งในบรรทัดอ้างอิง ClassLoader เนื่องจากการอ้างอิงนั้นเป็นแบบสแตติกไม่มีการรวบรวมขยะจะล้างข้อมูลอ้างอิงนี้ - ClassLoader และคลาสทั้งหมดที่โหลดอยู่ที่นี่

และหลังจากที่มี redeploys สองสามตัวเราพบ OutOfMemoryError

ตอนนี้ได้กลายเป็นปัญหาร้ายแรง ฉันสามารถตรวจสอบให้แน่ใจว่า Tomcat เริ่มต้นใหม่หลังจากการปรับใช้แต่ละครั้ง แต่จะลงเซิร์ฟเวอร์ทั้งหมดแทนที่จะเป็นเพียงแอปพลิเคชันที่ถูกปรับใช้ซ้ำซึ่งมักจะไม่สามารถทำได้

ดังนั้นฉันจึงรวบรวมวิธีการแก้ปัญหาในรหัสซึ่งทำงานบน Apache Tomcat 6.0 ฉันไม่ได้ทดสอบในเซิร์ฟเวอร์ของโปรแกรมอื่น ๆ และต้องเน้นว่านี้เป็นอย่างมากมีแนวโน้มที่จะไม่ทำงานโดยไม่มีการดัดแปลงบนเซิร์ฟเวอร์แอพลิเคชันอื่น

ฉันอยากจะบอกด้วยว่าฉันเกลียดรหัสนี้เป็นการส่วนตัวและไม่มีใครควรใช้สิ่งนี้เป็น "การแก้ไขด่วน" หากรหัสที่มีอยู่สามารถเปลี่ยนให้ใช้วิธีการปิดระบบและล้างข้อมูลที่เหมาะสม ครั้งเดียวที่ควรใช้คือหากมีห้องสมุดภายนอกที่รหัสของคุณขึ้นอยู่กับ (ในกรณีของฉันมันเป็นไคลเอนต์ RADIUS) ที่ไม่ได้ให้วิธีการล้างข้อมูลอ้างอิงคงที่ของตัวเอง

อย่างไรก็ตามด้วยรหัส สิ่งนี้ควรถูกเรียก ณ จุดที่แอพพลิเคชั่นไม่ได้ทำการปรับใช้ - เช่นเมธอดการทำลายของ servlet หรือ (วิธีการที่ดีกว่า) เมธอดของบริบทของ ServletContextListener ServletContextListener

//Get a list of all classes loaded by the current webapp classloader
WebappClassLoader classLoader = (WebappClassLoader) getClass().getClassLoader();
Field classLoaderClassesField = null;
Class clazz = WebappClassLoader.class;
while (classLoaderClassesField == null && clazz != null) {
    try {
        classLoaderClassesField = clazz.getDeclaredField("classes");
    } catch (Exception exception) {
        //do nothing
    }
    clazz = clazz.getSuperclass();
}
classLoaderClassesField.setAccessible(true);

List classes = new ArrayList((Vector)classLoaderClassesField.get(classLoader));

for (Object o : classes) {
    Class c = (Class)o;
    //Make sure you identify only the packages that are holding references to the classloader.
    //Allowing this code to clear all static references will result in all sorts
    //of horrible things (like java segfaulting).
    if (c.getName().startsWith("com.whatever")) {
        //Kill any static references within all these classes.
        for (Field f : c.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers())
                    && !Modifier.isFinal(f.getModifiers())
                    && !f.getType().isPrimitive()) {
                try {
                    f.setAccessible(true);
                    f.set(null, null);
                } catch (Exception exception) {
                    //Log the exception
                }
            }
        }
    }
}

classes.clear();

ฉันรู้ว่าเป็นเวลากว่า 8 ปีแล้วที่ฉันได้เขียนสิ่งนี้ แต่บางที่ในตอนนั้นและตอนนี้ฉันก็พบสาเหตุและวิธีการแก้ปัญหาที่ลึกกว่า นี่คือแม้ว่าคลาสทั้งหมดภายใน webapp จะเป็นเจ้าของโดย context classloader เธรดที่เรียกใช้การเริ่มต้นและปิดการเรียกกลับเป็นเจ้าของโดย parentloadload ซึ่งหมายความว่าหากรหัสการปิดระบบเริ่มต้นตัวแปรเธรดโลคัลสิ่งนี้จะทำให้ parent classloader สิ้นสุดการพักการอ้างอิงกับบริบท classloader เพื่อป้องกันการล้างข้อมูลที่ดี
Edward Torbett

โชคดีที่มีการแก้ไขที่ง่ายมาก - ย้ายโค้ดทั้งหมดในปัจจุบันในวิธีการปิดระบบไปยังวิธีการทำงานของเธรดวัตถุใหม่ จากนั้นในวิธีการปิดระบบให้เริ่มกระทู้นี้และรอให้เสร็จสมบูรณ์ โค้ดการล้างข้อมูลจะถูกเรียกใช้งานเหมือนกัน แต่ตัวแปรเธรดโลคัลใด ๆ จะยังคงเชื่อมโยงกับ classloader บริบทแทนการรั่วไหล
Edward Torbett

20

java.lang.OutOfMemoryError: PermGenข้อความแสดงให้เห็นว่าพื้นที่บริเวณ Generation ถาวรในหน่วยความจำของจะหมด

แอปพลิเคชัน Java ใด ๆ ที่ได้รับอนุญาตให้ใช้หน่วยความจำในปริมาณที่ จำกัด จำนวนหน่วยความจำที่แน่นอนที่แอปพลิเคชันของคุณสามารถใช้ระบุได้ในระหว่างการเริ่มต้นแอปพลิเคชัน

หน่วยความจำ Java ถูกแบ่งออกเป็นภูมิภาคต่าง ๆ ซึ่งสามารถเห็นได้ในรูปต่อไปนี้:

ป้อนคำอธิบายรูปภาพที่นี่

Metaspace: พื้นที่หน่วยความจำใหม่เกิดขึ้น

JDK 8 HotSpot JVM กำลังใช้หน่วยความจำดั้งเดิมสำหรับการแสดงข้อมูลเมตาของคลาสและเรียกว่า Metaspace คล้ายกับ Oracle JRockit และ IBM JVM

ข่าวดีก็คือมันไม่มีjava.lang.OutOfMemoryError: PermGenปัญหาเรื่องพื้นที่และคุณไม่จำเป็นต้องปรับแต่งและตรวจสอบพื้นที่หน่วยความจำนี้อีกต่อไปโดยใช้Java_8_Downloadหรือสูงกว่า


17

1) การเพิ่มขนาดหน่วยความจำ PermGen

สิ่งแรกที่เราทำได้คือทำให้ขนาดของพื้นที่ถาวรฮีปมีขนาดใหญ่ขึ้น สิ่งนี้ไม่สามารถทำได้ด้วย –Xms ปกติ (ตั้งค่าขนาดฮีพเริ่มต้น) และ –Xmx (ตั้งค่าขนาดฮีพสูงสุด) อาร์กิวเมนต์ JVM เนื่องจากดังกล่าวพื้นที่ฮีปการสร้างถาวรจะแยกออกจากพื้นที่ Java Heap ปกติทั้งหมดและชุดอาร์กิวเมนต์เหล่านี้ พื้นที่สำหรับพื้นที่ฮีป Java ปกตินี้ อย่างไรก็ตามมีข้อโต้แย้งที่คล้ายกันซึ่งสามารถใช้ (อย่างน้อยกับ Sun / OpenJDK jvms) เพื่อทำให้ขนาดของฮีปรุ่นถาวรมีขนาดใหญ่กว่า:

 -XX:MaxPermSize=128m

ค่าเริ่มต้นคือ 64m

2) เปิดใช้งานการกวาด

อีกวิธีหนึ่งในการดูแลสิ่งที่ดีคืออนุญาตให้ยกเลิกการโหลดคลาสเพื่อให้ PermGen ของคุณไม่มีวันหมด:

-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled

สิ่งที่ชอบเวทมนตร์ที่ทำงานให้ฉันในอดีต แม้ว่าสิ่งหนึ่งที่มีการแลกเปลี่ยนการปฏิบัติที่สำคัญในการใช้งานเหล่านั้นเนื่องจาก Permgen sweeps จะทำให้เหมือนคำขอเพิ่มอีก 2 คำขอสำหรับทุกคำขอที่คุณทำหรือบางสิ่งตามสายเหล่านั้น คุณจะต้องสมดุลการใช้งานของคุณกับการแลกเปลี่ยน

คุณสามารถค้นหารายละเอียดของข้อผิดพลาดนี้

http://faisalbhagat.blogspot.com/2014/09/java-outofmemoryerror-permgen.html


โพสต์ที่ยอดเยี่ยมของ @faisalbhagat faisalbhagat.blogspot.com/2014/09/…
leomeurer

ตัวเลือก 2 นั้นยอดเยี่ยม แต่เพิ่งได้รับการเตือนว่าไม่ควรใช้ในสภาพแวดล้อมการผลิต โดยทั่วไปแล้วดีที่สุดที่จะเก็บไว้ในสภาพแวดล้อมการพัฒนาเท่านั้น อย่างไรก็ตาม PermGen จะถูกลบออกจาก Java 8 openjdk.java.net/jeps/122
hdost

16

หรือคุณสามารถสลับไปที่ JRockit ซึ่งการจัดการดัดให้แตกต่างจากค่า jvm ของดวงอาทิตย์ โดยทั่วไปจะมีประสิทธิภาพที่ดีขึ้นเช่นกัน

http://www.oracle.com/technetwork/middleware/jrockit/overview/index.html


4
ในขณะที่ JRockit ไม่มี PermGen แต่สิ่งนี้จะไม่ช่วยในระยะยาว คุณจะได้รับjava.lang.OutOfMemoryError: There is insufficient native memoryแทน
stracktracer

14

ฉันมีปัญหาที่เรากำลังพูดถึงที่นี่สถานการณ์ของฉันคือ eclipse-helios + tomcat + jsf และสิ่งที่คุณทำคือการปรับใช้แอพพลิเคชั่นง่าย ๆ ให้กับ tomcat ฉันกำลังแสดงปัญหาเดียวกันที่นี่แก้ไขได้ดังนี้

ใน eclipse ไปที่แท็บเซิร์ฟเวอร์ดับเบิลคลิกที่เซิร์ฟเวอร์ที่ลงทะเบียนในกรณีของฉัน tomcat 7.0 จะเปิดไฟล์เซิร์ฟเวอร์ของฉันข้อมูลการลงทะเบียนทั่วไป ในส่วน"ข้อมูลทั่วไป"คลิกที่ลิงค์"เปิดการกำหนดค่าการเปิดตัว"นี่เป็นการเปิดใช้งานตัวเลือกเซิร์ฟเวอร์ในแท็บอาร์กิวเมนต์ในอาร์กิวเมนต์ VM ที่เพิ่มเข้ามาในท้ายที่สุดทั้งสองรายการ

-XX: MaxPermSize = 512m
-XX: PermSize = 512m

และพร้อม


14

คำตอบที่ง่ายที่สุดในวันนี้คือการใช้ Java 8

มันไม่สงวนหน่วยความจำเฉพาะสำหรับพื้นที่ PermGen เท่านั้นทำให้หน่วยความจำ PermGen ประสานกับพูลหน่วยความจำปกติได้อีกต่อไป

โปรดทราบว่าคุณจะต้องลบ-XXPermGen...=...พารามิเตอร์การเริ่มต้น JVM ที่ไม่ได้มาตรฐานทั้งหมดหากคุณไม่ต้องการให้ Java 8 บ่นว่าพวกเขาไม่ได้ทำอะไร


สวัสดีคำตอบนี้ได้รับแล้ว: stackoverflow.com/a/22897121/505893 โปรดลบคำตอบของคุณเพื่อความชัดเจน หากจำเป็นคุณสามารถปรับปรุงคำตอบที่ฉันพูดถึง ขอบคุณ;)
สีฟ้า

6
@ blueu ขอบคุณสำหรับการชี้ให้เห็น; อย่างไรก็ตามคำตอบนั้นไปด้านข้างทั้งหมดในการพูดคุยเกี่ยวกับ OutOfMemoryExceptions และเมตาดาต้ารั่ว นอกจากนี้ยังล้มเหลวในการพูดถึงจุดสำคัญของการลบตัวเลือก PermGen ในระยะสั้นฉันไม่แน่ใจว่าฉันจะปรับปรุงคำตอบ แต่แทนที่จะเขียนใหม่ ถ้ามันเป็นเพียงการสัมผัสอย่างรวดเร็วฉันจะรู้สึกลังเลน้อยลง แต่ดูเหมือนว่ามันจะมากกว่าการสัมผัสที่รวดเร็วและฉันก็เกลียดที่จะทำให้ผู้เขียนต้นฉบับขุ่นเคือง ยังรายการคำตอบนี้เป็นอาหารเย็นของสุนัขและบางทีการฆ่าโพสต์ของฉันจะดีที่สุด
Edwin Buck

8
  1. เปิด tomcat7w จากไดเรกทอรี bin ของ Tomcat หรือพิมพ์ Monitor Tomcat ในเมนูเริ่ม (หน้าต่างแบบแท็บจะเปิดขึ้นพร้อมกับข้อมูลบริการต่างๆ)
  2. ในพื้นที่ข้อความตัวเลือก Java ต่อท้ายบรรทัดนี้:

    -XX:MaxPermSize=128m
  3. ตั้งค่า Initial Memory Pool เป็น 1024 (เป็นทางเลือก)
  4. ตั้งค่าพูลหน่วยความจำสูงสุดเป็น 1024 (เลือกได้)
  5. คลิกตกลง
  6. เริ่มบริการ Tomcat ใหม่

6

ข้อผิดพลาดของพื้นที่ดัดระดับเกิดขึ้นเนื่องจากการใช้พื้นที่ขนาดใหญ่แทนที่จะเป็นพื้นที่ jvm เพื่อให้โค้ดทำงาน

ทางออกที่ดีที่สุดสำหรับปัญหานี้ในระบบปฏิบัติการ UNIX คือเปลี่ยนการกำหนดค่าบางอย่างในไฟล์ bash ขั้นตอนต่อไปนี้แก้ไขปัญหา

เรียกใช้คำสั่งgedit .bashrcบนเทอร์มินัล

สร้างJAVA_OTPSตัวแปรด้วยค่าต่อไปนี้:

export JAVA_OPTS="-XX:PermSize=256m -XX:MaxPermSize=512m"

บันทึกไฟล์ bash รันคำสั่ง exec bash บนเทอร์มินัล รีสตาร์ทเซิร์ฟเวอร์

ฉันหวังว่าวิธีการนี้จะช่วยแก้ปัญหาของคุณได้ หากคุณใช้ Java เวอร์ชันต่ำกว่า 8 ปัญหานี้จะเกิดขึ้นในบางครั้ง แต่ถ้าคุณใช้ Java 8 ปัญหาจะไม่เกิดขึ้น


5

การเพิ่มขนาดการสร้างแบบถาวรหรือการปรับแต่งพารามิเตอร์ GC จะไม่ช่วยถ้าคุณมีหน่วยความจำรั่วจริง หากแอปพลิเคชันของคุณหรือบางไลบรารีใช้กับบุคคลที่สามรั่วไหลตัวตักคลาสโซลูชันที่แท้จริงและถาวรเท่านั้นคือค้นหารอยรั่วนี้และแก้ไข มีเครื่องมือหลายอย่างที่สามารถช่วยคุณได้หนึ่งในล่าสุดคือPlumbrซึ่งเพิ่งเปิดตัวรุ่นใหม่ที่มีความสามารถที่จำเป็น


5

นอกจากนี้ถ้าคุณกำลังใช้ log4j ใน webapp ของคุณตรวจสอบวรรคนี้ใน log4j เอกสาร

ดูเหมือนว่าหากคุณใช้งานอยู่คุณจะPropertyConfigurator.configureAndWatch("log4j.properties")ทำให้หน่วยความจำรั่วเมื่อคุณเลิกใช้งานเว็บแอปของคุณ


4

ฉันมีการรวมกันของ Hibernate + Eclipse RCP ลองใช้-XX:MaxPermSize=512mและ-XX:PermSize=512mดูเหมือนว่าจะทำงานให้ฉัน


4

-XX:PermSize=64m -XX:MaxPermSize=128mชุด MaxPermSizeต่อมาคุณอาจลองเพิ่ม หวังว่ามันจะทำงาน งานเดียวกันสำหรับฉัน การตั้งค่าMaxPermSizeไม่ทำงานสำหรับฉันเท่านั้น


4

ฉันลองหลายคำตอบและสิ่งเดียวที่ในที่สุดงานก็คือการกำหนดค่านี้สำหรับปลั๊กอินคอมไพเลอร์ใน pom:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
        <fork>true</fork>
        <meminitial>128m</meminitial>
        <maxmem>512m</maxmem>
        <source>1.6</source>
        <target>1.6</target>
        <!-- prevent PermGen space out of memory exception -->
        <!-- <argLine>-Xmx512m -XX:MaxPermSize=512m</argLine> -->
    </configuration>
</plugin>

หวังว่าอันนี้จะช่วย


"argLine" ไม่รู้จักโดย maven-compiler-plugin 2.4 สนับสนุนเฉพาะ "compilerArgument" ซึ่งให้ข้อผิดพลาด: <compilerArgument> -XX: MaxPermSize = 256m </compilerArgument> [ERROR] ล้มเหลวในการเรียกใช้ javac แต่ไม่สามารถแยกวิเคราะห์ข้อผิดพลาด: -XX: MaxPermSize = 256m : javac <options> <source file>
Alex

หากขั้นตอนการรวบรวมของคุณใกล้หมดแล้ว permgen ให้ตั้งค่า <compilerArgument> บน maven-compiler-plugin หากการทดสอบหน่วยกำลังหมดเรี่ยวแรงตั้ง <argLine> ใน maven-surefire-plugin
qwerty

4

แก้ไขนี้สำหรับฉันเช่นกัน; อย่างไรก็ตามฉันสังเกตเห็นว่าเวลารีสตาร์ท servlet แย่ลงมากดังนั้นในขณะที่มันดีกว่าในการผลิตมันเป็นการลากในการพัฒนา


3

การกำหนดค่าหน่วยความจำขึ้นอยู่กับลักษณะของแอพของคุณ

คุณกำลังทำอะไร?

จำนวนการทำธุรกรรม precessed คืออะไร?

คุณโหลดข้อมูลเท่าใด

เป็นต้น

เป็นต้น

ฯลฯ

อาจเป็นไปได้ว่าคุณสามารถโปรไฟล์แอพของคุณและเริ่มล้างโมดูลบางส่วนจากแอพของคุณ

เห็นได้ชัดว่าสิ่งนี้อาจเกิดขึ้นได้หลังจากปรับใช้แอปพลิเคชันอีกครั้งสองสามครั้ง

Tomcat มีการปรับใช้ที่รวดเร็ว แต่ใช้หน่วยความจำ ลองรีสตาร์ทคอนเทนเนอร์ของคุณอีกครั้ง นอกจากนี้คุณจะต้องทราบจำนวนหน่วยความจำที่ต้องใช้ในโหมดการผลิตซึ่งดูเหมือนว่าเป็นเวลาที่ดีสำหรับการวิจัย


3

พวกเขาบอกว่าการปรับปรุง Tomcat รุ่นล่าสุด (6.0.28 หรือ 6.0.29) จัดการงานของการปรับใช้เซิร์ฟเล็ตใหม่ได้ดีขึ้นมาก


3

ฉันพบปัญหาเดียวกันทั้งหมด แต่น่าเสียดายที่ไม่มีคำแนะนำใดที่เหมาะกับฉันจริงๆ ปัญหาไม่ได้เกิดขึ้นระหว่างการปรับใช้และฉันก็ไม่ได้ทำการปรับใช้ที่น่าสนใจ

ในกรณีของฉันปัญหาเกิดขึ้นทุกครั้งที่จุดเดียวกันในระหว่างการดำเนินการของโปรแกรมประยุกต์บนเว็บของฉันในขณะที่เชื่อมต่อ (ผ่านจำศีล) ไปยังฐานข้อมูล

ลิงค์นี้ (ยังกล่าวถึงก่อนหน้านี้) ได้ให้ insides เพียงพอที่จะแก้ไขปัญหา การย้าย jdbc- (mysql) - เลื่อนออกจาก WEB-INF และเข้าไปในโฟลเดอร์ jre / lib / ext / ดูเหมือนว่าได้แก้ปัญหาแล้ว นี่ไม่ใช่โซลูชันที่สมบูรณ์แบบเนื่องจากการอัพเกรดเป็น JRE ที่ใหม่กว่าจะทำให้คุณต้องติดตั้งไดรเวอร์อีกครั้ง ตัวเลือกอื่นที่อาจทำให้เกิดปัญหาคล้ายกันคือ log4j ดังนั้นคุณอาจต้องการย้ายอันนั้นเช่นกัน


หากคุณไม่ต้องการรวมไดรเวอร์ใน jre / lib / ext คุณอาจได้ผลลัพธ์เดียวกันโดยรวมไดรเวอร์ในคลาสเริ่มต้นคอนเทนเนอร์ของคุณ java -cp /path/to/jdbc-mysql-driver.jar:/path/to/container/bootstrap.jar container.Start
ชาวสกอต

3

ขั้นตอนแรกในกรณีนี้คือการตรวจสอบว่า GC ได้รับอนุญาตให้ยกเลิกการโหลดคลาสจาก PermGen หรือไม่ JVM มาตรฐานค่อนข้างอนุรักษ์นิยมในเรื่องนี้ - คลาสเกิดมาเพื่อมีชีวิตตลอดไป ดังนั้นเมื่อโหลดแล้วคลาสจะยังคงอยู่ในหน่วยความจำแม้ว่าจะไม่ได้ใช้รหัสอีกต่อไป สิ่งนี้อาจกลายเป็นปัญหาเมื่อแอปพลิเคชันสร้างคลาสจำนวนมากแบบไดนามิกและไม่จำเป็นต้องใช้คลาสที่สร้างขึ้นเป็นระยะเวลานาน ในกรณีเช่นนี้การอนุญาตให้ JVM ยกเลิกการกำหนดคลาสจะมีประโยชน์ สิ่งนี้สามารถทำได้โดยการเพิ่มพารามิเตอร์การกำหนดค่าเพียงหนึ่งเดียวกับสคริปต์เริ่มต้นของคุณ:

-XX:+CMSClassUnloadingEnabled

โดยค่าเริ่มต้นนี้ถูกตั้งค่าเป็นเท็จและเพื่อเปิดใช้งานสิ่งนี้คุณต้องตั้งค่าตัวเลือกต่อไปนี้อย่างชัดเจนในตัวเลือก Java หากคุณเปิดใช้งาน CMSClassUnloadingEnabled, GC จะกวาด PermGen ด้วยและลบคลาสที่ไม่ได้ใช้อีกต่อไป โปรดทราบว่าตัวเลือกนี้จะใช้ได้เฉพาะเมื่อ UseConcMarkSweepGC เปิดใช้งานโดยใช้ตัวเลือกด้านล่าง ดังนั้นเมื่อรัน ParallelGC หรือ God ห้าม, Serial GC ตรวจสอบให้แน่ใจว่าคุณได้ตั้งค่า GC เป็น CMS โดยการระบุ:

-XX:+UseConcMarkSweepGC

3

การกำหนดให้หน่วยความจำเพิ่มเติมของ Tomcat ไม่ใช่โซลูชันที่เหมาะสม

ทางออกที่ถูกต้องคือทำการล้างข้อมูลหลังจากบริบทถูกทำลายและสร้างขึ้นใหม่ (การปรับใช้แบบร้อน) วิธีแก้ไขคือหยุดการรั่วไหลของหน่วยความจำ

หากเซิร์ฟเวอร์ Tomcat / Webapp ของคุณแจ้งว่าไม่สามารถยกเลิกการลงทะเบียนไดรเวอร์ (JDBC) ให้ยกเลิกการลงทะเบียน วิธีนี้จะหยุดการรั่วไหลของหน่วยความจำ

คุณสามารถสร้าง ServletContextListener และกำหนดค่าใน web.xml ของคุณ นี่คือตัวอย่าง ServletContextListener:

import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.log4j.Logger;

import com.mysql.jdbc.AbandonedConnectionCleanupThread;

/**
 * 
 * @author alejandro.tkachuk / calculistik.com
 *
 */
public class AppContextListener implements ServletContextListener {

    private static final Logger logger = Logger.getLogger(AppContextListener.class);

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
        logger.info("AppContextListener started");
    }

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        logger.info("AppContextListener destroyed");

        // manually unregister the JDBC drivers
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("Unregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error unregistering driver %s", driver), e);
            }

        }

        // manually shutdown clean up threads
        try {
            AbandonedConnectionCleanupThread.shutdown();
            logger.info("Shutting down AbandonedConnectionCleanupThread");
        } catch (InterruptedException e) {
            logger.warn("SEVERE problem shutting down AbandonedConnectionCleanupThread: ", e);
            e.printStackTrace();
        }        
    }
}

และที่นี่คุณกำหนดค่าใน web.xml ของคุณ:

<listener>
    <listener-class>
        com.calculistik.mediweb.context.AppContextListener 
    </listener-class>
</listener>  

2

"พวกเขา" ผิดเพราะฉันใช้ 6.0.29 และมีปัญหาเดียวกันแม้หลังจากตั้งค่าตัวเลือกทั้งหมดแล้ว ดังที่ทิมฮาวแลนด์ได้กล่าวไว้ข้างต้นตัวเลือกเหล่านี้ทำให้ไม่สามารถหลีกเลี่ยงได้ พวกเขาอนุญาตให้ฉันปรับใช้ซ้ำ 3 ครั้งก่อนที่จะกดปุ่มข้อผิดพลาดแทนทุกครั้งที่ฉันปรับใช้ซ้ำ


2

ในกรณีที่คุณจะได้รับใน IDE คราสแม้หลังจากตั้งค่าพารามิเตอร์ --launcher.XXMaxPermSize, -XX:MaxPermSizeฯลฯ ยังคงถ้าคุณได้รับข้อผิดพลาดเดียวกันก็มีโอกาสมากที่สุดคือการที่คราสจะใช้รุ่นรถของ JRE ซึ่งจะได้รับการติดตั้งโดยบางส่วน แอปพลิเคชันบุคคลที่สามและตั้งเป็นค่าเริ่มต้น รถบั๊กกี้รุ่นนี้ไม่รับพารามิเตอร์ PermSize และไม่ว่าคุณจะตั้งค่าอะไรก็ตามคุณยังคงได้รับข้อผิดพลาดของหน่วยความจำเหล่านี้ ดังนั้นใน eclipse.ini ของคุณให้เพิ่มพารามิเตอร์ต่อไปนี้:

-vm <path to the right JRE directory>/<name of javaw executable>

นอกจากนี้ตรวจสอบให้แน่ใจว่าคุณตั้งค่า JRE เริ่มต้นในการกำหนดค่าตามความชอบใน eclipse เป็นเวอร์ชันที่ถูกต้องของ java


2

วิธีเดียวที่ได้ผลกับฉันคือ JRockit JVM ฉันมี MyEclipse 8.6

ฮีปของ JVM เก็บอ็อบเจ็กต์ทั้งหมดที่สร้างโดยโปรแกรม Java ที่รันอยู่ Java ใช้newโอเปอเรเตอร์เพื่อสร้างออบเจ็กต์และหน่วยความจำสำหรับออบเจ็กต์ใหม่ได้รับการจัดสรรบนฮีป ณ รันไทม์ การรวบรวมขยะเป็นกลไกในการเพิ่มหน่วยความจำที่มีอยู่ในวัตถุที่ไม่ได้ถูกอ้างอิงโดยโปรแกรมอีกต่อไปโดยอัตโนมัติ


2

ฉันมีปัญหาที่คล้ายกัน Mine เป็น JDK 7 + Maven 3.0.2 + Struts 2.0 + โครงการ Google GUICE ที่ขึ้นอยู่กับการฉีด

เมื่อใดก็ตามที่ฉันพยายามเรียกใช้mvn clean packageคำสั่งมันก็แสดงข้อผิดพลาดดังต่อไปนี้และ"BUILD FAILURE"เกิดขึ้น

org.apache.maven.surefire.util.SurefireReflectionException: java.lang.reflect.InvocationTargetException; ข้อยกเว้นซ้อนคือ java.lang.reflect.InvocationTargetException: null java.lang.reflect.InvocationTargetException เกิดจาก: java.lang.OutOfMemoryError: พื้นที่ PermGen

ฉันลองใช้เคล็ดลับและลูกเล่นที่มีประโยชน์ทั้งหมดข้างต้น แต่น่าเสียดายที่ไม่มีวิธีใดที่เหมาะกับฉัน สิ่งที่ได้ผลสำหรับฉันได้อธิบายไว้ทีละขั้นตอนด้านล่าง: =>

  1. ไปที่ pom.xml ของคุณ
  2. ค้นหา <artifactId>maven-surefire-plugin</artifactId>
  3. เพิ่ม<configuration>องค์ประกอบใหม่แล้ว<argLine>องค์ประกอบย่อยที่ผ่าน-Xmx512m -XX:MaxPermSize=256mตามที่แสดงด้านล่าง =>

<configuration> <argLine>-Xmx512m -XX:MaxPermSize=256m</argLine> </configuration>

หวังว่าจะช่วยให้การเขียนโปรแกรมมีความสุข :)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.