Là đối tượng gộp một kỹ thuật không dùng nữa?


62

Tôi rất quen thuộc với khái niệm gộp đối tượng và tôi luôn cố gắng sử dụng nó nhiều nhất có thể.

Ngoài ra, tôi luôn nghĩ rằng nhóm đối tượng là chuẩn mực tiêu chuẩn như tôi đã quan sát thấy rằng chính Java cũng như các khung công tác khác sử dụng nhóm càng nhiều càng tốt.

Gần đây mặc dù tôi đã đọc một cái gì đó hoàn toàn mới (và phản trực giác?) Với tôi.

Việc gộp nhóm đó thực sự làm cho hiệu năng chương trình trở nên tồi tệ hơn, đặc biệt là trong các ứng dụng đồng thời và newthay vào đó, nên khởi tạo các đối tượng, vì trong các JVM mới hơn, việc khởi tạo một đối tượng thực sự rất nhanh.

Tôi đọc điều này trong cuốn sách: Java Concurrency in Practice

Bây giờ tôi bắt đầu suy nghĩ nếu tôi hiểu sai điều gì đó ở đây vì phần đầu tiên của cuốn sách đã khuyến khích sử Executorsdụng Threads tái sử dụng đó thay vì tạo ra các trường hợp mới.

Vì vậy, ngày nay đối tượng gộp lại trở nên phản đối?

Câu trả lời:


72

Nó không được coi là một kỹ thuật chung, bởi vì - như bạn nhận thấy - việc tạo và phá hủy các đối tượng tồn tại ngắn trên mỗi se (tức là cấp phát bộ nhớ và GC) là cực kỳ rẻ trong các JVM hiện đại. Vì vậy, việc sử dụng nhóm đối tượng viết tay cho các đối tượng chạy của bạn rất có thể chậm hơn, phức tạp hơn và dễ bị lỗi hơn so với đơn giản new. *

Mặc dù vậy, nó vẫn có những công dụng của nó, đối với các đối tượng đặc biệt có sự sáng tạo tương đối tốn kém, như kết nối DB / mạng, luồng, v.v.

* Một lần tôi phải cải thiện hiệu năng của ứng dụng Java đang thu thập dữ liệu. Điều tra đã phát hiện ra một nỗ lực sử dụng một nhóm đối tượng để phân bổ hàng triệu đối tượng ... và anh chàng thông minh đã viết nó đã sử dụng một khóa toàn cầu duy nhất để làm cho nó an toàn. Thay thế hồ bơi bằng đồng bằng newlàm cho ứng dụng nhanh hơn 30 lần.


1
Vì vậy, làm thế nào người ta có thể quyết định nếu khởi tạo của một đối tượng là quá đắt?
dùng10326

3
Nếu đối tượng tiêu thụ tài nguyên hệ điều hành (luồng, I / O, bộ nhớ dùng chung, v.v.)
kevin cline

13
@ user10326, bằng cách đo :-) Nếu việc tạo các đối tượng của bạn mất một thời gian ngắn và / hoặc chúng được liên kết với một số tài nguyên không có bộ nhớ đặc biệt, có khả năng giới hạn, bạn có thể xem xét gộp.
Péter Török

8
@ user10326, IMO trong hơn 95% trường hợp, các tiêu chí trên giúp bạn dễ dàng quyết định trước liệu bạn có cần một nhóm đối tượng hay không. (Hơn nữa, trong hầu hết tất cả các trường hợp cần một nhóm, rất có thể bạn sẽ sử dụng thư viện / khung hiện có, có thể nhóm đối tượng đã triển khai cho bạn.) Đối với phần còn lại, vẫn dễ dàng ẩn việc tạo đối tượng trong ví dụ một nhà máy, sau này có thể được thực hiện lại theo bất cứ cách nào bạn thấy phù hợp.
Péter Török

2
Điểm rất quan trọng được tạo bởi @Peter Torok: nhiều khung và thư viện triển khai nhóm cho bạn, LUÔN LUÔN đảm bảo rằng bạn chưa sử dụng thư viện gộp trước khi triển khai thư viện của riêng bạn.
hromanko

36

Câu trả lời cho câu hỏi cụ thể: 'Đối tượng có phải là một kỹ thuật không dùng nữa không?' Là:

Không. Nhóm đối tượng được sử dụng rộng rãi ở những nơi cụ thể - nhóm luồng, nhóm kết nối cơ sở dữ liệu, v.v.

Tạo đối tượng chung chưa bao giờ là một quá trình chậm. Tập hợp chính nó tiêu tốn tài nguyên - bộ nhớ và sức mạnh xử lý. Bất kỳ tối ưu hóa là một sự đánh đổi.

Quy tắc là:

Tối ưu hóa sớm là xấu xa !!!

Nhưng khi nào là tối ưu hóa sớm nhất định?

Tối ưu hóa sớm là bất kỳ tối ưu hóa nào được thực hiện, trước khi bạn phát hiện ra một nút cổ chai thông qua hồ sơ kỹ lưỡng .


2
Thật. OP nói "Tôi luôn cố gắng sử dụng nó nhiều nhất có thể" - đây là vấn đề, IMO.
nerdytenor

@Boris, Vì vậy, theo câu thứ hai của bạn, chúng ta không nên phản đối các kết nối và luồng db cho đến khi chúng ta phát hiện ra chúng như một nút cổ chai thông qua hồ sơ?
Pacerier

1
@Pac Một số kết quả định hình không cần đo lại liên tục :-)
David Bullock

9

Trong các tình huống mà bạn muốn tránh thu gom rác hoàn toàn, tôi nghĩ việc gộp đối tượng là giải pháp thay thế khả thi duy nhất. Vì vậy, không, nó hoàn toàn không phải là một kỹ thuật không dùng nữa.


1
Và tôi sẽ nói thêm rằng nên tránh GC bất cứ khi nào các đối tượng tồn tại đủ lâu để chúng chuyển sang thế hệ cũ.
Zan Lynx

8

Đo lường

Nó hoàn toàn phụ thuộc vào trường hợp sử dụng, kích thước của các đối tượng của bạn, JVM của bạn, các tùy chọn JVM của bạn, những gì bạn đã kích hoạt và một loạt các yếu tố khác.

Tóm lại: đo nó trước và đo sau. Giả sử bạn đang sử dụng khung tổng hợp đối tượng (như từ Apache) thì sẽ không quá khó để trao đổi giữa các lần triển khai.

Mẹo kiểm tra hiệu năng bổ sung - trước tiên hãy để JVM khởi động một chút, chạy thử nghiệm trên JVM đang chạy một số lần, nó có thể hoạt động khác đi.


3
"hãy để JVM khởi động một chút trước" - Tôi nhớ khi điều duy nhất phải "làm nóng" là màn hình. Oy, mọi thứ mới lại cũ.
kylben

Điều duy nhất tôi cần để làm nóng là cà phê!
vỡ mộng

@Marijn, Làm thế nào để bạn "hâm nóng"?
Pacerier

Xem Khung JMH để được giải thích đầy đủ ( openjdk.java.net/projects/code-tools/jmh ) nhưng về cơ bản, bạn phải cho JVM cơ hội để JIT mã của bạn, chạy GC trước khi điểm chuẩn của bạn và cứ thế.
Martijn Verburg

8

Việc gộp nhóm đó thực sự làm cho hiệu năng chương trình trở nên tồi tệ hơn, đặc biệt là trong các ứng dụng đồng thời và thay vào đó, nên khởi tạo các đối tượng mới, vì trong các JVM mới hơn, việc khởi tạo một đối tượng thực sự rất nhanh.

Phụ thuộc vào bối cảnh.


1
Câu trả lời tuyệt vời, và hợp tác. Tôi đã thêm (có lẽ, có thể là dấu hoa thị?) Rằng yêu cầu "24 byte" đề cập đến 4 trường hợp của số float 4 byte (16 byte), cộng với 4 byte cho tham chiếu đối tượng, cộng với 4 byte cho tham chiếu khóa. Đây là chi phí chính xác mà thiết kế của bạn loại bỏ.
strickli

5

Tôi không biết có một xu hướng thay đổi ở đây không nhưng chắc chắn nó sẽ là trường hợp mà nó phụ thuộc . Nếu lớp Java của bạn đang quản lý tài nguyên bên ngoài, chẳng hạn như kết nối RMI hoặc tải tệp tài nguyên, v.v. - thì chắc chắn chi phí cho việc khởi tạo đối tượng vẫn có thể cao (mặc dù các tài nguyên đó có thể được gộp chung cho bạn!). Như một thông lệ chung, tôi đồng ý với cuốn sách.


Bây giờ tôi không biết. Bởi vì ngay cả trong trường hợp này bạn mô tả cái nào (trước khi đọc) tôi chắc chắn sẽ sử dụng pooling, tôi cũng sẽ có chi phí chung.1) Các cấu trúc mới để xử lý gộp 2) Đồng bộ hóa cho đối tượng nhận / giải phóng từ pool 3) duy trì pool v.v ... Vì vậy, bây giờ tôi nghĩ rằng có lẽ không có trường hợp sử dụng nào hữu ích ngoại trừ việc lưu trữ một socket thay vì mở một cái mới mỗi lần để kết nối với máy chủ. Và trường hợp này là do mạng độ trễ và không phải là quá trình tạo tức thời
user10326

@ user10326 Có chính xác. Tôi thấy việc mở một ổ cắm như một phần của chi phí khởi tạo, nếu công việc của lớp phải làm điều đó và nó phải được khởi tạo trong hàm tạo thì tác động của độ trễ & IO là điều bạn quan tâm.
Jeremy
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.