เพื่อป้องกันการรั่วไหลของหน่วยความจำไดร์เวอร์ JDBC ไม่ได้ถูกบังคับให้ลงทะเบียน


325

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

SEVERE: เว็บแอปพลิเคชันลงทะเบียนไดรเวอร์ JBDC [oracle.jdbc.driver.OracleDriver] แต่ไม่สามารถยกเลิกการลงทะเบียนเมื่อเว็บแอปพลิเคชันหยุดทำงาน เพื่อป้องกันการรั่วไหลของหน่วยความจำไดร์เวอร์ JDBC ไม่ได้ถูกบังคับให้ลงทะเบียน

ความช่วยเหลือใด ๆ ชื่นชม


4
อาจซ้ำซ้อนกับstackoverflow.com/questions/2604630/…
skaffman

คำตอบ:


301

ตั้งแต่เวอร์ชั่น 6.0.24 เรือ Tomcat กับหน่วยความจำรั่วไหลของการตรวจสอบคุณสมบัติซึ่งจะสามารถนำไปสู่การชนิดของข้อความคำเตือนนี้เมื่อมีไดรเวอร์ JDBC 4.0 เข้ากันได้ใน webapp ของ/WEB-INF/libซึ่งอัตโนมัติลงทะเบียนตัวเองระหว่างการเริ่มต้น webapp โดยใช้ServiceLoaderAPIแต่ที่ ไม่ได้ลงทะเบียนโดยอัตโนมัติในระหว่างการปิดตัวของ webapp ข้อความนี้ไม่เป็นทางการล้วนๆ Tomcat ได้ทำการป้องกันการรั่วไหลของหน่วยความจำแล้ว

คุณทำอะไรได้บ้าง?

  1. ละเว้นคำเตือนเหล่านั้น Tomcat กำลังทำงานอย่างถูกต้อง ข้อบกพร่องที่เกิดขึ้นจริงนั้นอยู่ในรหัสของคนอื่น (ไดรเวอร์ JDBC ที่เป็นปัญหา) ไม่ใช่ของคุณ มีความสุขที่ Tomcat ทำงานได้อย่างถูกต้องและรอจนกว่าผู้จำหน่ายไดรเวอร์ JDBC จะได้รับการแก้ไขเพื่อให้คุณสามารถอัปเกรดไดรเวอร์ได้ บนมืออื่น ๆ ที่คุณไม่ควรจะลดลงไดรเวอร์ JDBC ใน webapp ของแต่เฉพาะในเซิร์ฟเวอร์/WEB-INF/lib /libหากคุณยังคงเก็บไว้ใน webapp ของแล้วคุณควรจะลงทะเบียนด้วยตนเองและยกเลิกการลงทะเบียนโดยใช้/WEB-INF/libServletContextListener

  2. ปรับลดรุ่นเป็น Tomcat 6.0.23 หรือเก่ากว่าเพื่อให้คุณไม่ต้องกังวลกับคำเตือนเหล่านั้น แต่มันจะเก็บความทรงจำที่รั่ว ไม่แน่ใจว่าเป็นสิ่งที่ดีที่จะรู้หลังจากทั้งหมด การรั่วไหลของหน่วยความจำประเภทนั้นเป็นหนึ่งในสาเหตุหลักที่อยู่เบื้องหลังOutOfMemoryErrorปัญหาระหว่าง Tomcat hotdeployments

  3. ย้ายไดรเวอร์ JDBC ไปยัง/libโฟลเดอร์ของ Tomcat และมีแหล่งข้อมูลพูลการเชื่อมต่อเพื่อจัดการไดรเวอร์ โปรดทราบว่า DBC ในตัวของ Tomcat ไม่ได้ลงทะเบียนไดรเวอร์อย่างถูกต้องเมื่อปิด ดูข้อผิดพลาดDBCP-322ซึ่งปิดเป็น WONTFIX คุณต้องการแทนที่ DBCP ด้วยพูลการเชื่อมต่ออื่นซึ่งทำงานได้ดีกว่า DBCP ยกตัวอย่างเช่นHikariCP , BoneCPหรือบางทีอาจจะTomcat JDBC สระว่ายน้ำ


25
นี่คือคำแนะนำที่ดี มันไม่ได้เตือนเรื่องการรั่วไหลของหน่วยความจำมันเป็นคำเตือนว่า Tomcat ดำเนินการบังคับบางอย่างเพื่อป้องกันการรั่วไหล
ด้าน b

49
ถ้าตัวเลือก (1) เป็นวิธีไปทำไม Tomcat บันทึกสิ่งเหล่านี้เป็น SEVERE แม้ว่า พบกับฉันหมายถึง "หน้าผู้ดูแลระบบ" ไม่ใช่ "เพิกเฉย"
Peter Becker

7
ทำไมไม่ทำด้วยตัวคุณเอง - แทนที่จะคาดหวังให้ Tomcat ทำ ในความคิดของฉันมันไม่ใช่หน้าที่ของ Tomcat ในการทำความสะอาดโค้ดที่ยุ่งเหยิงของเรา ดูคำตอบของฉันด้านล่าง
sparkyspider

2
@sproketboy: ใช่มั้ย คุณได้กำหนดสิ่งประดิษฐ์ JDBC เป็นฟิลด์ของคลาสที่ถูกเก็บไว้ในเซสชัน HTTP หรือไม่
BalusC

2
ฉันคิดว่ามันมักจะเป็นเหตุผล 3 (มีห้องสมุดใน wAR ตรงข้ามกับlib)
lapo

160

ในเมธอด servlet บริบทของคุณ contextDestroyed () วิธีการลงทะเบียนไดรเวอร์ด้วยตนเอง:

// This manually deregisters JDBC driver, which prevents Tomcat 7 from complaining about memory leaks wrto this class
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
    Driver driver = drivers.nextElement();
    try {
        DriverManager.deregisterDriver(driver);
        LOG.log(Level.INFO, String.format("deregistering jdbc driver: %s", driver));
    } catch (SQLException e) {
        LOG.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e);
    }
}

3
มันได้ผล! javabeat.net/servletcontextlistener-exampleอาจช่วยในการใช้ฟังบริบทของ servlet
Vadim Zin4uk

17
อาจไม่ปลอดภัยในสภาพแวดล้อมที่ใช้ร่วมกันเนื่องจากคุณอาจไม่ต้องการลงทะเบียนไดรเวอร์ JDBC ทั้งหมดที่มีอยู่ ดูคำตอบของฉันสำหรับวิธีการที่ปลอดภัยยิ่งขึ้น
daiscog

85

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

อย่างไรก็ตามวิธีการยกเลิกการลงทะเบียนไดรเวอร์ผ้าห่มเป็นอันตราย ไดรเวอร์บางตัวกลับมาโดยDriverManager.getDrivers()เมธอดนั้นอาจถูกโหลดโดย ClassLoader หลัก (เช่นตัวโหลดคอนเทนเนอร์ของ servlet) ไม่ใช่ ClassLoader ของบริบทของ webapp (เช่นพวกเขาอาจอยู่ในโฟลเดอร์ lib ของคอนเทนเนอร์ไม่ใช่ของ webapp และใช้ร่วมกันทั่วทั้งคอนเทนเนอร์ ) การลงทะเบียนสิ่งเหล่านี้จะส่งผลกระทบต่อแอปพลิเคชันเว็บอื่น ๆ ซึ่งอาจใช้งานได้ (หรือแม้แต่คอนเทนเนอร์เอง)

ดังนั้นหนึ่งควรตรวจสอบว่า ClassLoader สำหรับแต่ละไดรเวอร์เป็น ClassLoader ของ webapp ก่อนที่จะลงทะเบียน ดังนั้นในบริบทของ ContextListener () ของคุณเมธอด:

public final void contextDestroyed(ServletContextEvent sce) {
    // ... First close any background tasks which may be using the DB ...
    // ... Then close any DB connection pools ...

    // Now deregister JDBC drivers in this context's ClassLoader:
    // Get the webapp's ClassLoader
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    // Loop through all drivers
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
        Driver driver = drivers.nextElement();
        if (driver.getClass().getClassLoader() == cl) {
            // This driver was registered by the webapp's ClassLoader, so deregister it:
            try {
                log.info("Deregistering JDBC driver {}", driver);
                DriverManager.deregisterDriver(driver);
            } catch (SQLException ex) {
                log.error("Error deregistering JDBC driver {}", driver, ex);
            }
        } else {
            // driver was not registered by the webapp's ClassLoader and may be in use elsewhere
            log.trace("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader", driver);
        }
    }
}

ไม่ควรจะเป็นif (cl.equals(driver.getClass().getClassLoader())) {?
user11153

6
@ user11153 ไม่เรากำลังตรวจสอบว่ามันเป็นอินสแตนซ์ ClassLoader เดียวกันอย่างแม่นยำหรือไม่ถ้าเป็นสองอินสแตนซ์ที่แยกกันซึ่งมีค่าเท่ากัน
daiscog

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

ที่นี่ส่วนใหญ่เหมือนกัน แต่ด้วยรหัสการจัดการ MySQL / MariaDB เพิ่มเติม github.com/spring-projects/spring-boot/issues/2612
gavenkoa

2
หากคุณใช้ H2 หรือ PostgreSQL สิ่งนี้จะทำให้ไดรเวอร์ไม่ได้รับการลงทะเบียนอีกครั้งเมื่อทำการรีโหลด DriverManagerทั้งสองขับรถรักษาสถานะการลงทะเบียนภายในที่ไม่ได้ล้างถ้าคนขับมีการจดทะเบียนเลิกกิจการเพียงจาก ฉันออกความคิดเห็นรายละเอียดเพิ่มเติมได้ที่github.com/spring-projects/spring-boot/issues/ …
Marcel Stör

26

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

นี่คือวิธีที่ฉันทำ

ขั้นตอนที่ 1: ลงทะเบียนผู้ฟัง

web.xml

<listener>
    <listener-class>com.mysite.MySpecialListener</listener-class>
</listener>

ขั้นตอนที่ 2: ติดตั้ง Listener

com.mysite.MySpecialListener.java

public class MySpecialListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // On Application Startup, please…

        // Usually I'll make a singleton in here, set up my pool, etc.
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // On Application Shutdown, please…

        // 1. Go fetch that DataSource
        Context initContext = new InitialContext();
        Context envContext  = (Context)initContext.lookup("java:/comp/env");
        DataSource datasource = (DataSource)envContext.lookup("jdbc/database");

        // 2. Deregister Driver
        try {
            java.sql.Driver mySqlDriver = DriverManager.getDriver("jdbc:mysql://localhost:3306/");
            DriverManager.deregisterDriver(mySqlDriver);
        } catch (SQLException ex) {
            logger.info("Could not deregister driver:".concat(ex.getMessage()));
        } 

        // 3. For added safety, remove the reference to dataSource for GC to enjoy.
        dataSource = null;
    }

}

โปรดแสดงความคิดเห็นและ / หรือเพิ่ม ...


4
แต่DataSourceไม่มีcloseวิธี
Jim

9
มันควรจะดำเนินการ javax.servlet.ServletContextListener ไม่ขยาย ApplicationContextListener?
egaga

2
มีเหตุผลสำหรับคำสั่งของการดำเนินการเหล่านั้นในวิธีการcontextDestroyedหรือไม่? ทำไมคุณทำขั้นตอนที่ 1 ก่อนที่จะทำขั้นตอนที่ 2, ที่initContext, envContextและdatasourceไม่ได้อ้างถึงที่ทั้งหมดหรือไม่ ฉันถามเพราะฉันไม่เข้าใจขั้นตอนที่ 3
matthaeus

4
@ matthaeus ฉันคิดว่าขั้นตอนที่ 1 ไม่จำเป็นlookupดูเหมือนว่าจะได้รับวัตถุบางอย่างที่คุณไม่ต้องการ ขั้นตอนที่ 3 นั้นไร้ประโยชน์อย่างสมบูรณ์ แน่นอนว่ามันจะไม่เพิ่มความปลอดภัยใด ๆ และดูเหมือนว่าผู้เริ่มต้นจะทำสิ่งที่ไม่เข้าใจว่า GC ทำงานอย่างไร ฉันจะไปกับstackoverflow.com/a/5315467/897024และลบไดรเวอร์ทั้งหมด
kapex

4
@kapep การลบไดรเวอร์ทั้งหมดเป็นสิ่งที่อันตรายเนื่องจากบางคนอาจแชร์ข้ามคอนเทนเนอร์ ดูคำตอบของฉันสำหรับวิธีการที่จะลบไดรเวอร์ที่โหลดโดย ClassLoader ของ webapp ของคุณเท่านั้น
daiscog

14

นี่คือปัญหาการลงทะเบียนไดรเวอร์ / การยกเลิกการลงทะเบียนอย่างหมดจดในไดรเวอร์ของ mysql หรือ tomcats webapp-classloader คัดลอกไดรเวอร์ mysql ลงในโฟลเดอร์ libcats lib (ดังนั้นโหลดโดย jvm โดยตรงไม่ใช่โดย tomcat) และข้อความจะหายไป นั่นทำให้ไดรเวอร์ mysql jdbc ถูกยกเลิกการโหลดเฉพาะเมื่อปิด JVM เท่านั้นและไม่มีใครสนใจเรื่องหน่วยความจำรั่ว


2
ที่ไม่ทำงาน ... ฉันพยายามที่จะคัดลอกไดรเวอร์ jdbc มีคุณพูดว่า: TOMCAT_HOME / lib / postgresql-9.0-801.jdbc4.jar - Tomcat 7.0 \ lib \ postgresql-9.0-801.jdbc4.jar ที่ไม่มีผลลัพธ์ ...
FAjir

5
@Florito - คุณต้องลบออกจากเว็บแอปของคุณ WEB-INF / lib เช่นกัน
Collin Peters

8

หากคุณได้รับข้อความนี้จากสงคราม Maven ที่สร้างขึ้นให้เปลี่ยนขอบเขตของไดรเวอร์ JDBC ที่ระบุไว้และวางสำเนาของมันไว้ในไดเรกทอรี lib แบบนี้:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.18</version>
  <!-- put a copy in /usr/share/tomcat7/lib -->
  <scope>provided</scope>
</dependency>

8

โซลูชันสำหรับการปรับใช้ต่อแอป

นี่คือผู้ฟังที่ฉันเขียนเพื่อแก้ไขปัญหา: ตรวจสอบอัตโนมัติถ้าไดรเวอร์ได้ลงทะเบียนตัวเองและดำเนินการตามนั้น

สำคัญ: มีไว้เพื่อใช้งานเฉพาะเมื่อมีการปรับใช้ jar ไดร์เวอร์ใน WEB-INF / libไม่ใช่ใน Tomcat / lib ตามที่แนะนำเพื่อให้แต่ละแอปพลิเคชันสามารถดูแลไดร์เวอร์ของตัวเองและรันบน Tomcat ที่ไม่มีใครแตะต้อง . นั่นคือวิธีที่ควรจะเป็น IMHO

เพียงกำหนดค่าฟังใน web.xml ของคุณก่อนและเพลิดเพลิน

เพิ่มใกล้ส่วนบนของweb.xml :

<listener>
    <listener-class>utils.db.OjdbcDriverRegistrationListener</listener-class>    
</listener>

บันทึกเป็นutils / db / OjdbcDriverRegistrationListener.java :

package utils.db;

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 oracle.jdbc.OracleDriver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Registers and unregisters the Oracle JDBC driver.
 * 
 * Use only when the ojdbc jar is deployed inside the webapp (not as an
 * appserver lib)
 */
public class OjdbcDriverRegistrationListener implements ServletContextListener {

    private static final Logger LOG = LoggerFactory
            .getLogger(OjdbcDriverRegistrationListener.class);

    private Driver driver = null;

    /**
     * Registers the Oracle JDBC driver
     */
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        this.driver = new OracleDriver(); // load and instantiate the class
        boolean skipRegistration = false;
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            if (driver instanceof OracleDriver) {
                OracleDriver alreadyRegistered = (OracleDriver) driver;
                if (alreadyRegistered.getClass() == this.driver.getClass()) {
                    // same class in the VM already registered itself
                    skipRegistration = true;
                    this.driver = alreadyRegistered;
                    break;
                }
            }
        }

        try {
            if (!skipRegistration) {
                DriverManager.registerDriver(driver);
            } else {
                LOG.debug("driver was registered automatically");
            }
            LOG.info(String.format("registered jdbc driver: %s v%d.%d", driver,
                    driver.getMajorVersion(), driver.getMinorVersion()));
        } catch (SQLException e) {
            LOG.error(
                    "Error registering oracle driver: " + 
                            "database connectivity might be unavailable!",
                    e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Deregisters JDBC driver
     * 
     * Prevents Tomcat 7 from complaining about memory leaks.
     */
    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        if (this.driver != null) {
            try {
                DriverManager.deregisterDriver(driver);
                LOG.info(String.format("deregistering jdbc driver: %s", driver));
            } catch (SQLException e) {
                LOG.warn(
                        String.format("Error deregistering driver %s", driver),
                        e);
            }
            this.driver = null;
        } else {
            LOG.warn("No driver to deregister");
        }

    }

}

คำแนะนำ: ในฐานะของ Servlet 3.0 คุณสามารถใส่คำอธิบายประกอบชั้นเรียนของคุณด้วย@WebListenerและละเว้นการweb.xmlกำหนดค่า
Basil Bourque

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

6

ฉันจะเพิ่มสิ่งนี้ที่ฉันพบในฟอรัม Spring หากคุณย้ายไดร์เวอร์ jar JDBC ของคุณไปยังโฟลเดอร์ tomcat lib แทนที่จะปรับใช้กับ webapp ของคุณคำเตือนดูเหมือนว่าจะหายไป ฉันสามารถยืนยันได้ว่าสิ่งนี้ได้ผลสำหรับฉัน

http://forum.springsource.org/showthread.php?87335-Failure-to-unregister-the-MySQL-JDBC-Driver&p=334883#post334883


1
ฉันคิดว่านี่เป็นทางออกที่ดีกว่า - เพียงซับคลาส DatasourceManager ของคุณและแทนที่เมธอด close เพื่อเพิ่มการลงทะเบียนซ้ำ จากนั้น Spring จะจัดการกับมันเมื่อมันทำลายบริบทของมันและ Tomcat จะไม่ให้บันทึก SEVERE แก่คุณและคุณไม่จำเป็นต้องย้ายไดรเวอร์ JDBC ของคุณไปยัง lib dir นี่คือข้อความดั้งเดิมจากกระดานสนทนาเก่าของฤดูใบไม้ผลิ
Adam

6

ฉันพบว่าการใช้วิธีการทำลายง่าย () เพื่อยกเลิกการลงทะเบียนไดรเวอร์ JDBC ใด ๆ ที่ทำงานได้ดี

/**
 * Destroys the servlet cleanly by unloading JDBC drivers.
 * 
 * @see javax.servlet.GenericServlet#destroy()
 */
public void destroy() {
    String prefix = getClass().getSimpleName() +" destroy() ";
    ServletContext ctx = getServletContext();
    try {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while(drivers.hasMoreElements()) {
            DriverManager.deregisterDriver(drivers.nextElement());
        }
    } catch(Exception e) {
        ctx.log(prefix + "Exception caught while deregistering JDBC drivers", e);
    }
    ctx.log(prefix + "complete");
}

5
อาจไม่ปลอดภัยในสภาพแวดล้อมที่ใช้ร่วมกันเนื่องจากคุณอาจไม่ต้องการลงทะเบียนไดรเวอร์ JDBC ทั้งหมดที่มีอยู่ ดูคำตอบของฉันสำหรับวิธีการที่ปลอดภัยยิ่งขึ้น นอกจากนี้สิ่งนี้ควรทำใน ServletContextListener ไม่ใช่บนพื้นฐานต่อ servlet เนื่องจากไดร์เวอร์ JDBC ของคุณถูกแชร์ไปยังเซอร์เล็ตทั้งหมดในเว็บแอปของคุณ
daiscog

3

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

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mywebsite</groupId>
    <artifactId>emusicstore</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <source>1.9</source>
                    <target>1.9</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <!-- ... -->

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.0.1.Final</version>
        </dependency>

        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>1.0.1.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

</project>

MyWebAppContextListener.java

package com.emusicstore.utils;

import com.mysql.cj.jdbc.AbandonedConnectionCleanupThread;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;

public class MyWebAppContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("************** Starting up! **************");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("************** Shutting down! **************");
        System.out.println("Destroying Context...");
        System.out.println("Calling MySQL AbandonedConnectionCleanupThread checkedShutdown");
        AbandonedConnectionCleanupThread.checkedShutdown();

        ClassLoader cl = Thread.currentThread().getContextClassLoader();

        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();

            if (driver.getClass().getClassLoader() == cl) {
                try {
                    System.out.println("Deregistering JDBC driver {}");
                    DriverManager.deregisterDriver(driver);

                } catch (SQLException ex) {
                    System.out.println("Error deregistering JDBC driver {}");
                    ex.printStackTrace();
                }
            } else {
                System.out.println("Not deregistering JDBC driver {} as it does not belong to this webapp's ClassLoader");
            }
        }
    }

}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <listener>
        <listener-class>com.emusicstore.utils.MyWebAppContextListener</listener-class>
    </listener>

<!-- ... -->

</web-app>

แหล่งที่มาที่เป็นแรงบันดาลใจให้ฉันสำหรับการแก้ไขข้อบกพร่องนี้


2

ฉันมีปัญหาที่คล้ายกัน แต่นอกจากนี้ฉันได้รับข้อผิดพลาด Java Heap Space ทุกครั้งที่ฉันแก้ไข / บันทึกหน้า JSP ด้วยเซิร์ฟเวอร์ Tomcat ที่ทำงานอยู่ดังนั้นบริบทจึงไม่ได้รับการชาร์จใหม่ทั้งหมด

เวอร์ชันของฉันคือ Apache Tomcat 6.0.29 และ JDK 6u12

การอัพเกรด JDK เป็น6u21ตามที่แนะนำในส่วนการอ้างอิงของ URL http://wiki.apache.org/tomcat/MemoryLeakProtectionแก้ไขปัญหา Java Heap Space (บริบทตอนนี้โหลด OK อีกครั้ง) แม้ว่าข้อผิดพลาด JDBC Driver จะยังคงปรากฏขึ้น


0

ฉันพบปัญหาเดียวกันกับ Tomcat รุ่น 6.026

ฉันใช้ Mysql JDBC.jar ใน WebAPP Library และ TOMCAT Lib

ในการแก้ไขปัญหาข้างต้นโดยลบ Jar ออกจากโฟลเดอร์ TOMCAT lib

ดังนั้นสิ่งที่ฉันเข้าใจคือ TOMCAT จัดการหน่วยความจำ JDBC รั่วไหลอย่างถูกต้อง แต่ถ้า jar Jdbc ของ MYSQL ซ้ำกันใน WebApp และ Tomcat Lib Tomcat จะสามารถจัดการ jar ที่มีอยู่ในโฟลเดอร์ Tomcat Lib เท่านั้น


0

ฉันประสบปัญหานี้เมื่อฉันติดตั้งแอปพลิเคชัน Grails ของฉันบน AWS นี้เป็นเรื่องของคนขับเริ่มต้น JDBC org.h2คนขับ ตามที่คุณเห็นใน Datasource.groovy ภายในโฟลเดอร์การกำหนดค่าของคุณ ตามที่คุณเห็นด้านล่าง:

dataSource {
    pooled = true
    jmxExport = true
    driverClassName = "org.h2.Driver"   // make this one comment
    username = "sa"
    password = ""
}

แสดงความคิดเห็นบรรทัดเหล่านั้นทุกที่ที่มีการกล่าวถึงorg.h2.Driverในไฟล์ datasource.groovy หากคุณไม่ได้ใช้ฐานข้อมูลนั้น มิฉะนั้นคุณต้องดาวน์โหลดไฟล์ jar ฐานข้อมูลนั้น

ขอบคุณ


0

ข้อผิดพลาดนี้เกิดขึ้นกับฉันในแอปพลิเคชัน Grails ที่มีไดรเวอร์ JTDS 1.3.0 (SQL Server) ปัญหาคือการเข้าสู่ระบบที่ไม่ถูกต้องใน SQL Server หลังจากแก้ปัญหานี้ (ใน SQL Server) แอปของฉันถูกปรับใช้อย่างถูกต้องใน Tomcat เคล็ดลับ: ฉันเห็นข้อผิดพลาดใน stacktrace.log

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