2011-10-17 21 views
36

Khi tôi triển khai lại ứng dụng của tôi trong tomcat, tôi nhận được vấn đề sau đây:Memory rò rỉ khi redeploying ứng dụng trong Tomcat

The web application [] created a ThreadLocal with key of type 
[java.lang.ThreadLocal] (value [[email protected]]) 
and a value of type [com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty] 
(value [[email protected]a183d2]) but 
failed to remove it when the web application was stopped. 
This is very likely to create a memory leak. 

Ngoài ra, đang sử dụng ehcache trong ứng dụng của tôi. Điều này cũng có vẻ dẫn đến ngoại lệ sau.

 SEVERE: The web application [] created a ThreadLocal with key of type [null] 
    (value [[email protected]]) and a value of type [java 
    .util.WeakHashMap... 

Ehcache dường như tạo bản đồ băm yếu và tôi nhận được thông báo rằng điều này rất có khả năng gây ra rò rỉ bộ nhớ.

Tôi đã tìm kiếm trên mạng và tìm thấy điều này, http://jira.pentaho.com/browse/PRD-3616 nhưng tôi không có quyền truy cập vào máy chủ như vậy.

Vui lòng cho tôi biết nếu các cảnh báo này có bất kỳ tác động chức năng nào hoặc chúng có thể bị bỏ qua không? Tôi đã sử dụng tùy chọn "Tìm rò rỉ bộ nhớ" trong trình quản lý tomcat và thông báo "Không tìm thấy rò rỉ bộ nhớ"

+1

Những cảnh báo có nghĩa là khả năng của bạn để triển khai các ứng dụng mà không cần khởi động lại Tomcat chính nó là hạn chế. Các ứng dụng web từ lâu đã bị cản trở do rò rỉ bộ nhớ của loại này. Chúng không có tác động trừ khi bạn triển khai lại ứng dụng. Tôi không biết, nhưng tôi nghi ngờ những thông điệp này trong đầu ra của Tomcat, bắt đầu xuất hiện một hoặc hai năm nữa, là gây áp lực lên các nhà xây dựng khung để bắt đầu dọn dẹp chính xác sau khi họ khởi động lại. –

Trả lời

36

Khi bạn triển khai lại ứng dụng, Tomcat sẽ tạo trình nạp lớp mới. Trình nạp lớp cũ phải được thu thập rác, nếu không bạn sẽ bị rò rỉ bộ nhớ permgen.

Tomcat không thể kiểm tra xem bộ sưu tập rác có hoạt động hay không, nhưng nó biết về một số lỗi phổ biến. Nếu trình nạp lớp webapp đặt một ThreadLocal với một cá thể có lớp được nạp bởi trình nạp lớp webapp, thì chuỗi servlet giữ một tham chiếu đến cá thể đó. Điều này có nghĩa là trình nạp lớp sẽ không được thu thập rác.

Tomcat thực hiện một số phát hiện như vậy, xem here for more information. Làm sạch người dân địa phương chủ đề là khó khăn, bạn sẽ phải gọi remove() trên ThreadLocal trong mỗi chủ đề được truy cập từ đó. Trong thực tế, điều này chỉ quan trọng trong quá trình phát triển khi bạn triển khai lại ứng dụng web nhiều lần. Trong sản xuất, bạn có thể không triển khai lại, vì vậy điều này có thể bị bỏ qua.

Để thực sự tìm ra trường hợp nào xác định địa chỉ của luồng, bạn phải sử dụng trình thu thập thông tin. Ví dụ: bộ đệm heap trong JProfiler (tuyên bố từ chối trách nhiệm: công ty của tôi phát triển JProfiler) sẽ giúp bạn tìm những người dân địa phương chủ đề đó. Chọn lớp giá trị được báo cáo (com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty hoặc com.sun.xml.bind.v2.ClassFactory) và hiển thị các tham chiếu đến được tích lũy. Một trong số đó sẽ là java.lang.ThreadLocal$ThreadLocalMap$Entry. Chọn đối tượng được tham chiếu cho loại tham chiếu đến đó và chuyển sang chế độ xem phân bổ. Bạn sẽ thấy nơi mà cá thể đã được cấp phát. Với thông tin đó bạn có thể quyết định xem bạn có thể làm điều gì đó về nó hay không.

enter image description here

+2

Như tôi thấy, có vẻ như JProfiler không phải là giải pháp nguồn mở miễn phí. Bạn có thể đề xuất một số thay thế khác – Raghav

+3

Hãy thử [VisualVM] (http://visualvm.java.net/). – timomeinen

+0

@Raghav Eclipse Memory Analyzer cũng là một công cụ tuyệt vời để phân tích các vùng heap. http://www.eclipse.org/mat/ – Phil

2

Tôi đoán bạn có thể thấy điều này nhưng chỉ trong trường hợp ehcache doc khuyến cáo để đưa lib trong tomcat và không có trong thư mục WEB-INF/lib: http://ehcache.org/documentation/integrations/tomcat

+0

Cập nhật liên kết: http://ehcache.org/generated/2.9.0/html/ehc-all/#page/Ehcache_Documentation_Set%2Fco-tcat_tomcat_issues_and_practices.html%23wwconnect_header rồi cuộn xuống 'rò rỉ bộ nhớ của trình nạp lớp' –

3

Tạo Chủ đề mà không làm sạch chúng một cách chính xác sẽ cuối cùng đã giúp bạn thoát khỏi trí nhớ - đã ở đó, thực hiện điều đó.

Những người vẫn đang tự hỏi cho nhanh giải pháp/cách giải quyết, có thể đi cho dưới đây:

  • Nếu chạy tomcat độc, giết javaw.exe hoặc quá trình mang nó.
  • Nếu chạy từ nhật thực, hãy xóa eclipse.exe và java.exe hoặc quy trình kèm theo.
  • Vẫn chưa được giải quyết, Kiểm tra trình quản lý tác vụ, có khả năng quy trình gây ra điều này sẽ được hiển thị với bộ nhớ cao nhất cách sử dụng - Thực hiện phân tích và tiêu diệt điều đó.

Bạn nên triển khai lại nội dung và tiếp tục mà không gặp phải vấn đề về bộ nhớ.

6

Mattias Jiderhamn có một số tuyệt vời 6-part article giải thích rõ ràng lý thuyết và thực hành về rò rỉ bộ nạp lớp. Thậm chí tốt hơn, anh cũng phát hành một tệp jar mà chúng tôi có thể đưa vào các tệp chiến tranh của mình. Tôi đã thử nó trên các ứng dụng web của tôi, và các tập tin jar làm việc như một say mê! Tệp jar được gọi là classloader-leak-prevention.jar. Để sử dụng nó cũng đơn giản như chỉ cần thêm này để web.xml của chúng tôi

<listener> 
    <listener-class>se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor</listener-class> 
</listener> 

và sau đó thêm này để pom.xml của chúng tôi

<dependency> 
    <groupId>se.jiderhamn</groupId> 
    <artifactId>classloader-leak-prevention</artifactId> 
    <version>1.15.2</version> 
</dependency> 

Để biết thêm thông tin, vui lòng tham khảo project home page hosted on GitHub hoặc Part 6 of his article

1

Tôi khuyên bạn nên khởi tạo thread locals, trong một ServletRequestListener.

ServletRequestListener có 2 phương pháp: một để khởi tạo và một để hủy.

Bằng cách này, bạn có thể dọn dẹp ThreadLocal. Ví dụ:

public class ContextInitiator implements ServletRequestListener { 
    @Override 
    public void requestInitialized(ServletRequestEvent sre) { 
     context = new ThreadLocal<ContextThreadLocal>() { 
      @Override 
      protected ContextThreadLocal initialValue() { 
       ContextThreadLocal context = new ContextThreadLocal(); 
       return context; 
      } 
     }; 
     context.get().setRequest(sre.getServletRequest()); 
    } 
    @Override 
    public void requestDestroyed(ServletRequestEvent sre) { 
     context.remove(); 
    } 
} 

web.xml:

<listener> 
    <listener-class>ContextInitiator</listener-class> 
</listener> 
Các vấn đề liên quan