Dỡ bỏ các lớp trong java?


174

Tôi có một trình tải lớp tùy chỉnh để ứng dụng máy tính để bàn có thể tự động bắt đầu tải các lớp từ Máy chủ ứng dụng mà tôi cần nói chuyện. Chúng tôi đã làm điều này vì số lượng lọ được yêu cầu để làm điều này là vô lý (nếu chúng tôi muốn gửi chúng). Chúng tôi cũng gặp vấn đề về phiên bản nếu chúng tôi không tải các lớp một cách linh hoạt trong thời gian chạy từ thư viện AppServer.

Bây giờ, tôi vừa gặp phải một vấn đề mà tôi cần nói chuyện với hai AppServers khác nhau và thấy rằng tùy thuộc vào lớp nào tôi tải trước, tôi có thể bị hỏng nặng ... Có cách nào để buộc dỡ lớp mà không thực sự giết JVM không?

Hy vọng điều này có ý nghĩa


Bạn có một trình nạp lớp cho mỗi bình không? Làm thế nào để OSGI chứa các tệp lưu trữ để dỡ bộ nạp lớp? Dường như không có API dỡ tải trong lớp trình nạp lớp?
hetaoblog

Câu trả lời:


190

Cách duy nhất mà Class có thể được dỡ tải là nếu Classloader được sử dụng là rác được thu thập. Điều này có nghĩa là, các tham chiếu đến mỗi lớp đơn lẻ và chính trình nạp lớp cần phải đi theo con đường của dodo.

Một giải pháp khả thi cho vấn đề của bạn là có Trình tải lớp cho mỗi tệp jar và Trình nạp lớp cho mỗi Trình ứng dụng ủy nhiệm việc tải các lớp thực tế cho các trình nạp lớp Jar cụ thể. Bằng cách đó, bạn có thể trỏ đến các phiên bản khác nhau của tệp jar cho mọi máy chủ Ứng dụng.

Điều này không tầm thường, mặc dù. Nền tảng OSGi cố gắng làm điều này, vì mỗi gói có một trình nạp lớp khác nhau và các phụ thuộc được giải quyết bởi nền tảng. Có lẽ một giải pháp tốt sẽ là xem xét nó.

Nếu bạn không muốn sử dụng OSGI, một cách triển khai có thể là sử dụng một thể hiện của lớp JarClassloader cho mỗi tệp JAR.

Và tạo một lớp MultiClassloader mới, mở rộng Classloader. Lớp này bên trong sẽ có một mảng (hoặc Danh sách) của JarClassloaders và trong phương thức notifyClass () sẽ lặp qua tất cả các trình nạp lớp bên trong cho đến khi có thể tìm thấy một định nghĩa hoặc ném NoClassDefFoundException. Một vài phương thức truy cập có thể được cung cấp để thêm JarClassloaders mới vào lớp. Có một số triển khai có thể có trên mạng cho MultiClassLoader, do đó bạn thậm chí có thể không cần phải viết riêng.

Nếu bạn kích hoạt MultiClassloader cho mọi kết nối đến máy chủ, về nguyên tắc, có thể mọi máy chủ đều sử dụng một phiên bản khác nhau của cùng một lớp.

Tôi đã sử dụng ý tưởng MultiClassloader trong một dự án, trong đó các lớp có chứa các tập lệnh do người dùng định nghĩa phải được tải và tải khỏi bộ nhớ và nó hoạt động khá tốt.


31
Cũng lưu ý rằng theo java.sun.com/docs/books/jls/second_edition/html/ không tải các lớp là một tối ưu hóa và, tùy thuộc vào việc triển khai JVM, có thể thực sự xảy ra.

5
Là một giải pháp thay thế dễ dàng và nhẹ hơn cho OSGi, hãy thử Mô-đun JBoss - tải lớp được mô đun hóa với trình nạp lớp cho mỗi mô-đun (một nhóm các lọ).
Ondra ižka

42

Có, có nhiều cách để tải các lớp và "dỡ" chúng sau này. Bí quyết là triển khai trình nạp lớp riêng của bạn nằm giữa trình nạp lớp cấp cao (trình tải lớp Hệ thống) và trình nạp lớp của (các) máy chủ ứng dụng và hy vọng rằng các trình nạp lớp của máy chủ ứng dụng sẽ ủy thác tải lớp cho trình tải trên .

Một lớp được định nghĩa bởi gói, tên của nó và trình nạp lớp mà nó đã tải ban đầu. Lập trình trình nạp lớp "proxy", là trình đầu tiên được tải khi khởi động JVM. Quy trình làm việc:

  • Chương trình bắt đầu và lớp "chính" thực sự được tải bởi trình nạp lớp proxy này.
  • Mỗi lớp sau đó được tải bình thường (nghĩa là không thông qua triển khai trình nạp lớp khác có thể phá vỡ cấu trúc phân cấp) sẽ được ủy quyền cho trình nạp lớp này.
  • Các đại biểu trình nạp lớp proxy java.xsun.xcho trình nạp lớp hệ thống (chúng không được tải thông qua bất kỳ trình nạp lớp nào khác ngoài trình nạp lớp hệ thống).
  • Đối với mỗi lớp có thể thay thế, hãy khởi tạo một trình nạp lớp (thực sự tải lớp đó và không ủy thác nó cho trình nạp lớp cha) và tải nó qua đây.
  • Lưu trữ gói / tên của các lớp dưới dạng các khóa và trình nạp lớp dưới dạng các giá trị trong cấu trúc dữ liệu (ví dụ Hashmap).
  • Mỗi khi trình nạp lớp proxy nhận được yêu cầu cho một lớp đã được tải trước đó, nó sẽ trả về lớp từ trình nạp lớp được lưu trữ trước đó.
  • Nó đủ để xác định vị trí mảng byte của một lớp bằng trình tải lớp của bạn (hoặc "xóa" cặp khóa / giá trị khỏi cấu trúc dữ liệu của bạn) và tải lại lớp trong trường hợp bạn muốn thay đổi nó.

Hoàn thành ngay tại đó không nên đến ClassCastException hoặc LinkageError, v.v.

Để biết thêm thông tin về hệ thống phân cấp trình tải lớp (vâng, đó chính xác là những gì bạn đang triển khai ở đây; -) hãy xem "Lập trình Java dựa trên máy chủ" của Ted Neward - cuốn sách đó đã giúp tôi thực hiện một cái gì đó rất giống với những gì bạn muốn.


3
Tôi không hiểu mọi người, người đã đánh dấu -1 cho câu trả lời này mà không để lại nhận xét. Đối với tôi có vẻ tốt. Có lẽ có một cái ClassLoadercho mỗi lớp là quá nhiều, một cái ClassLoadercho mỗi JAR có ý nghĩa. Có thể cụ thể hơn về cách buộc tải lên lớp trong lược đồ được đề xuất? Ví dụ: làm thế nào tôi có thể đảm bảo, các thể hiện của các lớp được tải bởi ClassLoaderA không được giới thiệu bởi các thể hiện được tải bởi ClassLoaderB?
dma_k

@dma_k chính xác, câu trả lời là tốt, nhưng nó không chạm vào những điểm chính mà bạn đề cập.
nhấp nháy

@Georgi có một triển khai hiện có mà chúng ta có thể kích hoạt / tái sử dụng cho việc này không?
Sled

1
Sẽ rất hữu ích, nếu bạn có thể cung cấp mã java mẫu. Nói chính xác, tôi đang tìm kiếm Cách dỡ các lớp bằng CustomClassLoader nhưng không gặp may.
Sriharsha grv

@ Sriharshag.rv Bạn đã thử điều này và thực hiện một mẫu?
niaomingjian

17

Tôi đã viết một trình nạp lớp tùy chỉnh, từ đó có thể hủy tải các lớp riêng lẻ mà không cần trình nạp lớp. Trình nạp lớp Jar


Hoạt động như một bùa mê :). Có phương pháp nào để dỡ tất cả các tệp lớp của một tệp jar không?
Ercksen

Đáng tiếc là không phải lúc này. Nhưng sẽ nhìn vào nó. Có thể trong các phiên bản tương lai.
Kamran

Nhân tiện, tôi tìm thấy một cách giải quyết nhỏ. Nếu bạn có một JarClassLoadertệp cho mỗi tệp jar bạn đã tải, bạn có thể gọi getLoadedClasses()nó, sau đó lặp lại từng tệp và dỡ bỏ nó.
Ercksen

12

Trình tải lớp có thể là một vấn đề khó khăn. Bạn đặc biệt có thể gặp sự cố nếu bạn đang sử dụng nhiều trình nạp lớp và không có các tương tác rõ ràng và được xác định rõ ràng. Tôi nghĩ rằng để thực sự có thể dỡ một lớp bạn sẽ đi, phải xóa tất cả các tham chiếu đến bất kỳ lớp nào (và các thể hiện của chúng) mà bạn đang cố gắng dỡ.

Hầu hết mọi người cần phải thực hiện loại điều này kết thúc bằng OSGi . OSGi thực sự mạnh mẽ và nhẹ đáng ngạc nhiên và dễ sử dụng,


7

Bạn có thể dỡ tải ClassLoader nhưng bạn không thể tải các lớp cụ thể. Cụ thể hơn, bạn không thể tải các lớp được tạo trong Trình tải lớp không thuộc quyền kiểm soát của bạn.

Nếu có thể, tôi khuyên bạn nên sử dụng ClassLoader của riêng bạn để bạn có thể dỡ tải.


4

Các lớp có một tham chiếu mạnh mẽ ngầm định đến cá thể ClassLoader của chúng và ngược lại. Chúng là rác được thu thập như với các đối tượng Java. Nếu không nhấn giao diện công cụ hoặc tương tự, bạn không thể xóa từng lớp.

Như bạn có thể bị rò rỉ bộ nhớ. Bất kỳ tham chiếu mạnh mẽ nào đến một trong các lớp hoặc trình nạp lớp của bạn sẽ rò rỉ toàn bộ. Điều này xảy ra với các triển khai Sun của ThreadLocal, ví dụ java.sql.DriverManager và java.beans.


-1

Nếu bạn đang theo dõi trực tiếp nếu lớp dỡ tải hoạt động trong JConsole hoặc một cái gì đó, hãy thử thêm java.lang.System.gc()vào cuối logic dỡ lớp của bạn. Nó rõ ràng kích hoạt Garbage Collector.


3
Cẩn thận: System.gc () không cần thiết gọi GC. Nó chỉ yêu cầu jvm khởi động nó, nhưng nó không thực thi nó. Và IME thường không bắt đầu GC: - \
Juh_
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.