Tôi đã có một ứng dụng java ee khá lớn với một đường dẫn lớn, xử lý nhiều xml. Hiện tại tôi đang cố gắng tăng tốc một số chức năng của mình và định vị các đường dẫn mã chậm thông qua các trình biên dịch lấy mẫu.
Một điều tôi nhận thấy là đặc biệt là các phần trong mã của chúng tôi trong đó chúng tôi có các cuộc gọi như TransformerFactory.newInstance(...)
rất chậm. Tôi đã theo dõi điều này để FactoryFinder
phương pháp findServiceProvider
luôn tạo ra một ServiceLoader
thể hiện mới . Trong ServiceLoader
javadoc tôi đã tìm thấy các lưu ý sau về bộ nhớ đệm:
Các nhà cung cấp được định vị và khởi động một cách lười biếng, đó là, theo yêu cầu. Trình tải dịch vụ duy trì bộ đệm của các nhà cung cấp đã được tải cho đến nay. Mỗi lời gọi của phương thức iterator trả về một trình vòng lặp đầu tiên mang lại tất cả các phần tử của bộ đệm, theo thứ tự khởi tạo, sau đó lười biếng định vị và khởi tạo bất kỳ nhà cung cấp nào còn lại, lần lượt thêm từng bộ vào bộ đệm. Bộ nhớ cache có thể được xóa thông qua phương thức tải lại.
Càng xa càng tốt. Đây là một phần của FactoryFinder#findServiceProvider
phương thức OpenJDKs :
private static <T> T findServiceProvider(final Class<T> type)
throws TransformerFactoryConfigurationError
{
try {
return AccessController.doPrivileged(new PrivilegedAction<T>() {
public T run() {
final ServiceLoader<T> serviceLoader = ServiceLoader.load(type);
final Iterator<T> iterator = serviceLoader.iterator();
if (iterator.hasNext()) {
return iterator.next();
} else {
return null;
}
}
});
} catch(ServiceConfigurationError e) {
...
}
}
Mỗi cuộc gọi để findServiceProvider
gọi ServiceLoader.load
. Điều này tạo ra một ServiceLoader mới mỗi lần. Bằng cách này, có vẻ như không sử dụng cơ chế bộ nhớ đệm ServiceLoaders nào cả. Mỗi cuộc gọi sẽ quét đường dẫn lớp cho ServiceProvider được yêu cầu.
Những gì tôi đã thử:
- Tôi biết bạn có thể thiết lập một thuộc tính hệ thống như
javax.xml.transform.TransformerFactory
để chỉ định một triển khai cụ thể. Bằng cách này, Factory Downloader không sử dụng quy trình ServiceLoader và siêu nhanh. Đáng buồn thay, đây là một thuộc tính rộng jvm và ảnh hưởng đến các quá trình java khác đang chạy trong jvm của tôi. Ví dụ: ứng dụng của tôi giao hàng với Saxon và nên sử dụngcom.saxonica.config.EnterpriseTransformerFactory
Tôi đã có một ứng dụng khác không giao hàng với Saxon. Ngay khi tôi đặt thuộc tính hệ thống, ứng dụng khác của tôi không khởi động được, vì không cócom.saxonica.config.EnterpriseTransformerFactory
đường dẫn lớp của nó. Vì vậy, điều này dường như không phải là một lựa chọn cho tôi. - Tôi đã cấu trúc lại mọi nơi mà a
TransformerFactory.newInstance
được gọi và lưu vào bộ biến áp. Nhưng có nhiều nơi khác nhau trong phần phụ thuộc của tôi, nơi tôi không thể cấu trúc lại mã.
Câu hỏi của tôi là: Tại sao Factory Downloader không sử dụng lại ServiceLoader? Có cách nào để tăng tốc toàn bộ quá trình ServiceLoader này ngoài việc sử dụng các thuộc tính hệ thống không? Không thể thay đổi điều này trong JDK để FactorySense sử dụng lại một cá thể ServiceLoader? Ngoài ra, điều này không cụ thể cho một Factory Downloader duy nhất. Bahaviour này là giống nhau cho tất cả các lớp Factory Downloader trong javax.xml
gói tôi đã xem xét cho đến nay.
Tôi đang sử dụng OpenJDK 8/11. Các ứng dụng của tôi được triển khai trong một ví dụ Tomcat 9.
Chỉnh sửa: Cung cấp thêm chi tiết
Đây là ngăn xếp cuộc gọi cho một lệnh gọi XMLInputFactory.newInstance duy nhất:
Nơi mà hầu hết các tài nguyên được sử dụng là trong ServiceLoaders$LazyIterator.hasNextService
. Phương thức này gọi getResources
ClassLoader để đọc META-INF/services/javax.xml.stream.XMLInputFactory
tệp. Cuộc gọi đó chỉ mất khoảng 35ms mỗi lần.
Có cách nào để hướng dẫn Tomcat lưu trữ tốt hơn các tệp này để chúng được phục vụ nhanh hơn không?
-D
cờ cho Tomcat
quy trình của mình chưa? Ví dụ: -Djavax.xml.transform.TransformerFactory=<factory class>.
Không nên ghi đè các thuộc tính cho các ứng dụng khác. Bài viết của bạn được mô tả tốt và có lẽ bạn đã thử nó nhưng tôi muốn xác nhận. Xem Cách đặt thuộc tính hệ thống Javax.xml.transform.TransformerFactory , Cách đặt Đối số HeapMemory hoặc JVM trong Tomcat