Một biểu thức lambda có tạo ra một đối tượng trên heap mỗi khi nó được thực thi không?


182

Khi tôi lặp lại một bộ sưu tập bằng cách sử dụng đường cú pháp mới của Java 8, chẳng hạn như

myStream.forEach(item -> {
  // do something useful
});

Điều này có tương đương với đoạn trích 'cú pháp cũ' bên dưới không?

myStream.forEach(new Consumer<Item>() {
  @Override
  public void accept(Item item) {
    // do something useful
  }
});

Điều này có nghĩa là một Consumerđối tượng ẩn danh mới được tạo trên heap mỗi khi tôi lặp qua một bộ sưu tập? Làm thế nào nhiều không gian heap này mất? Nó có ý nghĩa hiệu suất gì? Có nghĩa là tôi nên sử dụng kiểu cũ cho các vòng lặp khi lặp trên các cấu trúc dữ liệu đa cấp lớn?


48
Câu trả lời ngắn gọn: không. Đối với lambdas không trạng thái (những người không nắm bắt được bất cứ điều gì từ bối cảnh từ vựng của họ), chỉ có một trường hợp sẽ được tạo ra (một cách lười biếng) và được lưu trữ tại trang web chụp. (Đây là cách triển khai hoạt động; thông số kỹ thuật được viết cẩn thận để cho phép, nhưng không bắt buộc, cách tiếp cận này.)
Brian Goetz

Câu trả lời:


158

Nó tương đương nhưng không giống nhau. Nói một cách đơn giản, nếu một biểu thức lambda không nắm bắt được các giá trị, nó sẽ là một singleton được sử dụng lại trên mỗi lệnh gọi.

Hành vi không được chỉ định chính xác. JVM được tự do lớn về cách triển khai nó. Hiện tại, JVM của Oracle tạo (ít nhất) một thể hiện cho mỗi biểu thức lambda (nghĩa là không chia sẻ thể hiện giữa các biểu thức giống hệt nhau khác nhau) nhưng tạo ra các singletons cho tất cả các biểu thức không thu được giá trị.

Bạn có thể đọc câu trả lời này để biết thêm chi tiết. Ở đó, tôi không chỉ đưa ra một mô tả chi tiết hơn mà còn kiểm tra mã để quan sát hành vi hiện tại.


Điều này được đề cập trong Đặc tả ngôn ngữ Java®, chương 15,27.4. Run-time Đánh giá Lambda Expressions

Tóm tắt:

Các quy tắc này nhằm cung cấp tính linh hoạt cho việc triển khai ngôn ngữ lập trình Java, theo đó:

  • Một đối tượng mới không cần phải được phân bổ trên mỗi đánh giá.

  • Các đối tượng được tạo bởi các biểu thức lambda khác nhau không cần phải thuộc các lớp khác nhau (ví dụ: nếu các cơ thể giống hệt nhau).

  • Mọi đối tượng được tạo ra bởi đánh giá không cần thuộc về cùng một lớp (ví dụ, các biến cục bộ có thể được nội tuyến).

  • Nếu một "thể hiện hiện tại" có sẵn, thì nó không cần phải được tạo ra trong một đánh giá lambda trước đó (ví dụ, nó có thể đã được phân bổ trong quá trình khởi tạo của lớp kèm theo).


24

Khi một thể hiện đại diện cho lambda được tạo ra một cách nhạy cảm phụ thuộc vào nội dung chính xác của cơ thể lambda của bạn. Cụ thể, yếu tố chính là những gì lambda thu được từ môi trường từ vựng. Nếu nó không nắm bắt bất kỳ trạng thái nào có thể thay đổi từ sáng tạo sang sáng tạo, thì một thể hiện sẽ không được tạo ra mỗi khi vòng lặp for-mỗi được nhập. Thay vào đó, một phương thức tổng hợp sẽ được tạo ra tại thời điểm biên dịch và trang web sử dụng lambda sẽ chỉ nhận được một đối tượng singleton ủy nhiệm cho phương thức đó.

Lưu ý thêm rằng khía cạnh này phụ thuộc vào việc triển khai và bạn có thể mong đợi các cải tiến và tiến bộ trong tương lai trên HotSpot hướng tới hiệu quả cao hơn. Có các kế hoạch chung để tạo ra một đối tượng nhẹ mà không có một lớp tương ứng đầy đủ, có đủ thông tin để chuyển tiếp đến một phương thức duy nhất.

Đây là một bài viết tốt, có thể truy cập sâu về chủ đề:

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood


0

Bạn đang truyền một thể hiện mới cho forEachphương thức. Mỗi khi bạn làm điều đó, bạn tạo một đối tượng mới nhưng không phải là một đối tượng cho mỗi lần lặp. Lặp lại được thực hiện bên trong forEachphương thức bằng cách sử dụng cùng một đối tượng 'gọi lại' cho đến khi nó được thực hiện với vòng lặp.

Vì vậy, bộ nhớ được sử dụng bởi vòng lặp không phụ thuộc vào kích thước của bộ sưu tập.

Điều này có tương đương với đoạn trích 'cú pháp cũ' không?

Đúng. Nó có một chút khác biệt ở mức rất thấp nhưng tôi không nghĩ bạn nên quan tâm đến chúng. Biểu thức Lamba sử dụng tính năng inv invocate thay vì các lớp ẩn danh.


Bạn có bất kỳ tài liệu chỉ định này? Đó là sự tối ưu hóa khá thú vị.
A. Rama

Cảm ơn, nhưng nếu tôi có một bộ sưu tập các bộ sưu tập, ví dụ như khi thực hiện tìm kiếm theo chiều sâu trên cơ sở hạ tầng cây thì sao?
Bastian Voigt

2
@ A.Rama Xin lỗi, tôi không thấy tối ưu hóa. Nó giống nhau với hoặc không có lambdas và có hoặc không có vòng lặp forEach.
aalku

1
Nó không thực sự giống nhau, nhưng vẫn cần nhiều nhất một đối tượng cho mỗi cấp độ lồng nhau bất kỳ lúc nào, không đáng kể. Mỗi lần lặp mới của một vòng lặp bên trong sẽ tạo ra một đối tượng mới, rất có thể là bắt được mục hiện tại từ vòng lặp bên ngoài. Điều này tạo ra một số áp lực GC, nhưng vẫn không có gì để thực sự lo lắng.
Marko Topolnik

4
@aalku: " Mỗi khi bạn làm điều đó bạn tạo ra một đối tượng mới ": Không theo câu trả lời này của Holgernhận xét này của Brian Goetz .
Lii
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.