2017-01-18 15 views
20

Tôi có WebApplication được triển khai trong Tomcat 7.0.70. Tôi đã mô phỏng tình huống sau:Rò rỉ bộ nhớ khi triển khai lại ứng dụng trong Tomcat

  1. Tôi đã tạo vùng lưu trữ.
  2. Sau đó, tôi đã gửi yêu cầu Http và trong phương thức của dịch vụ, tôi đã in luồng hiện tại và trình nạp lớp của nó. Và sau đó tôi gọi Thread.currentThread.sleep (10000).
  3. Và tại cùng thời điểm tôi đã nhấp vào 'không triển khai ứng dụng này' trong trang quản trị của Tomcat.
  4. Tôi đã tạo kết xuất vùng heap mới.
  5. Sau một vài phút, tôi đã tạo ra bãi chứa hep mới.


KẾT QUẢ


Chủ đề bãi

Trên màn hình sau đây bạn có thể thấy rằng sau khi tôi nhấp vào "triển khai lại", tất cả các chủ đề (mà đã được liên kết với ứng dụng web này) đã bị giết trừ chuỗi "http-apr-8081-exec-10". Khi tôi đặt thuộc tính của Tomcat là "renewThreadsWhenStoppingContext == true", vì vậy bạn có thể thấy rằng sau một thời gian luồng này ("http-apr-8081-exec-10") đã bị giết và luồng mới (http-apr-8081-exec-11)) được tạo ra thay vì nó. Vì vậy, tôi đã không mong đợi để có WCL cũ sau khi tạo ra đống đống 3, bởi vì không có bất kỳ chủ đề cũ hoặc các đối tượng.

enter image description here

Heapd đổ 1

Trên hai màn hình sau đây bạn có thể thấy rằng khi ứng dụng đang chạy chỉ có một WCL (tham số của nó "bắt đầu" = true). Và chuỗi "http-apr-8081-exec-10" có contextClassLoader = URLClassLoader (vì nó nằm trong nhóm của Tomcat). Tôi chỉ nói về chủ đề này vì bạn có thể thấy rằng chuỗi này sẽ xử lý yêu cầu HTTP trong tương lai của tôi.

enter image description here

enter image description here

Gửi yêu cầu HTTP

Bây giờ tôi gửi yêu cầu HTTP và trong mã của tôi tôi nhận được thông tin về các thread.You hiện tại có thể thấy rằng yêu cầu của tôi là đang được xử lý theo chuỗi "http-apr-8081-exec-10"

дек 23, 2016 9:28:16 AM c.c.c.f.s.r.ReportGenerationServiceImpl INFO: request has been handled in 
    thread = http-apr-8081-exec-10, its contextClassLoader = WebappClassLoader 
    context: /hdi 
    delegate: false 
    repositories: 
    /WEB-INF/classes/ 
    ----------> Parent Classloader: [email protected] 

Sau đó, tôi nhấp vào "Triển khai lại ứng dụng web của tôi" và tôi nhận được thông báo sau trong bảng điều khiển.

дек 23, 2016 9:28:27 AM org.apache.catalina.loader.WebappClassLoaderBase clearReferencesThreads 
SEVERE: The web application [/hdi] appears to have started a thread named [http-apr-8081-exec-10] but has failed to stop it. This is very likely to create a memory leak. 

Heapd đổ 2

Trên màn hình sau đây bạn có thể thấy rằng có hai trường hợp WebAppClassLoader. Một trong số đó (số # 1) là cũ (thuộc tính của nó "started" = false). Và WCL # 2 được tạo sau khi triển khai lại ứng dụng (thuộc tính của nó "started" = true). Và chuỗi chúng tôi xem xét có contextClassLoader = "org.apache.catalina.loader.WebappClassLoader". Tại sao? Tôi dự kiến ​​sẽ thấy contextClassLoader = "java.net.URLClassLoader" (sau khi tất cả, khi bất kỳ thread nào kết thúc công việc của nó, nó được trả về hồ bơi của Tomcat và thuộc tính "contextClassLoader" được đặt thành bất kỳ trình nạp lớp cơ sở nào).

enter image description here

enter image description here

enter image description here

Heapd đổ 3

Bạn có thể thấy rằng có không phải là thread "http-Tháng tư-8081-exec-10", nhưng có chuỗi "http-apr-8081-exec-11" và nó có contextClassLoader = "WebappClassLoader" (Tại sao không phải URLClassLoader?).

Cuối cùng, chúng tôi có những điều sau đây: có chuỗi "http-apr-8081-exec-11" có tham chiếu đến WebappClassLoader # 1. Và obviosly khi tôi thực hiện "Root GC gần" trên WCL # 1 Tôi sẽ thấy những ref để thread 11.

enter image description here

enter image description here

câu hỏi.

Làm cách nào tôi có thể nói với Tomcat trả về giá trị cũ contextClassLoader (URLClassLoader) sau khi chuỗi sẽ kết thúc công việc?

Làm cách nào để đảm bảo Tomcat không sao chép giá trị cũ "contextClassLoader" trong quá trình gia hạn chuỗi?

Có thể, bạn có biết cách nào khác để giải quyết vấn đề của mình không?

+0

Có thể trùng lặp của [Chủ đề trong Tomcat. Rò rỉ bộ nhớ] (http://stackoverflow.com/questions/41223141/threads-in-tomcat-memory-leaks) –

+0

Nếu bạn không nhận được đủ sự chú ý lần đầu tiên, có lẽ bạn có thể bắt đầu một tiền thưởng. Không chỉ đăng lại cùng một câu hỏi. –

+0

@ChristopherSchultz, bài đăng này cung cấp thêm thông tin. –

Trả lời

0

Rò rỉ bộ nhớ trong quá trình redeploing của tomcat là vấn đề rất cũ. Cách duy nhất thực sự để giải quyết nó là khởi động lại tomcat thay vì ứng dụng redeploy. Nếu bạn có một số ứng dụng, bạn cần phải chạy một số dịch vụ của tomcat trên các cổng khác nhau và tham gia nó với nginx.

+0

Chúng tôi đang làm việc với một trong các máy chủ Java EE từ 8 năm nay và ** chúng tôi đã viết một quy trình mà máy chủ ứng dụng được khởi động lại với từng triển khai trong môi trường sản xuất **. – bilelovitch

3

Tomcat thường không phải là lựa chọn tốt trên môi trường sản xuất. Tôi đã sử dụng Tomcat trên một vài ứng dụng sản xuất và tôi thấy rằng ngay cả khi kích thước heap và các cấu hình khác được thiết lập đúng - và mỗi khi bạn tải lại ứng dụng, mức tiêu thụ bộ nhớ tăng lên. Cho đến khi bạn không khởi động lại dịch vụ tomcat, bộ nhớ không được khai hoang hoàn toàn. Chúng tôi đã thử nghiệm tất cả các thử nghiệm như vậy, xóa nhật ký, triển khai lại tất cả ứng dụng, thường xuyên khởi động lại tomcat mỗi tháng một lần hoặc một tuần trong thời gian ít bận rộn nhất. Nhưng cuối cùng tôi phải nói rằng chúng tôi đã chuyển môi trường sản xuất của mình sang Glassfish và WebSphere.

tôi hy vọng bạn sẽ đã đã trải qua các trang này:

Memory leak in a Java web application

Tomcat Fix Memory Leak?

https://developers.redhat.com/blog/2014/08/14/find-fix-memory-leaks-java-application/

http://www.tomcatexpert.com/blog/2010/04/06/tomcats-new-memory-leak-prevention-and-detection

Nếu các ứng dụng web của bạn không được kết hợp chặt chẽ với Tomcat sau đó bạn có thể nghĩ đến việc sử dụng anothe r container web. Bây giờ chúng tôi sử dụng Glassfish ngay cả trên máy phát triển và sản xuất và ngày chúng tôi đưa ra quyết định này, chúng tôi đã tiết kiệm rất nhiều thời gian của mình. Mặc dù Glassfish và các máy chủ khác mất nhiều thời gian hơn trong khi chúng bắt đầu vì chúng không nhẹ như Tomcat nhưng sau khi cuộc sống dễ dàng hơn một chút.

+0

Tôi đồng ý với bạn rằng đó là sự cố Tomcat đã biết. Nhưng chúng tôi vẫn sử dụng Tomcat trong sản xuất vì nó nhẹ và hiệu quả. Đơn giản là chúng tôi không bao giờ triển khai lại ứng dụng trên Tomcat sản xuất mà chỉ cần khởi động lại thùng chứa tomcat. Điều đó có ý nghĩa bởi vì chúng là các ứng dụng được sử dụng nhiều và chi phí của vùng chứa là chấp nhận được. –

0

Chúng tôi có hàng trăm trường hợp Tomcat chạy trong một số môi trường (cũng sản xuất) và giải pháp hợp lý duy nhất mà chúng tôi phát hiện ra là dừng và khởi động lại mọi Tomcat vào một thời điểm định sẵn hàng ngày (vào ban đêm).

Chúng tôi đã thử nhiều thủ thuật, nhưng đây là giải pháp lâu dài cho các yêu cầu thời gian hoạt động của chúng tôi.

0

Tomcat thường không phải là lựa chọn tốt trên môi trường sản xuất. Tôi đã sử dụng Tomcat trên một vài ứng dụng sản xuất và tôi thấy rằng ngay cả khi kích thước heap và các cấu hình khác được thiết lập đúng - và mỗi khi bạn tải lại ứng dụng, mức tiêu thụ bộ nhớ tăng lên. Cho đến khi bạn không khởi động lại dịch vụ tomcat, bộ nhớ không được khai hoang hoàn toàn. Chúng tôi đã thử nghiệm tất cả các thử nghiệm như vậy, xóa nhật ký, triển khai lại tất cả ứng dụng, thường xuyên khởi động lại tomcat mỗi tháng một lần hoặc một tuần trong thời gian ít bận rộn nhất. Nhưng cuối cùng tôi phải nói rằng chúng tôi đã chuyển môi trường sản xuất của mình sang Glassfish và WebSphere.

1

Từ kinh nghiệm của tôi với vấn đề này, những gì đã ngăn ngừa tomcat đúng GC trình tải lớp cũ hơn là một số ThreadLocal s một vài khung tôi đang sử dụng đã tạo (và không xử lý đúng).

Something tương tự như những gì được giải thích ở đây: ThreadLocal & Memory Leak

tôi đã cố gắng để hoàn thành đúng này ThreadLocal s và rò rỉ của tôi giảm rất nhiều. Nó vẫn còn rò rỉ, nhưng tôi có thể xử lý gấp 10 lần redeploys hơn trước đây.

Tôi chắc chắn sẽ kiểm tra bộ nhớ của bạn với các đối tượng có thể được kết nối bằng cách nào đó đến ThreadLocal s (chúng rất phổ biến, đặc biệt nếu bạn sử dụng cái gì đó để kiểm soát giao dịch hoặc bất kỳ thứ gì được phân tách).

Tôi hy vọng điều đó sẽ hữu ích!

Các vấn đề liên quan