2010-08-01 27 views
7

Gần đây, tôi bắt đầu học một số Java. Từ những gì tôi đã học về JVM, có vẻ như JIT làm cho nó khá nhanh trên các hoạt động đòi hỏi chu kỳ CPU (tức là gọi một phương thức) mà còn làm cho nó đói cho bộ nhớ. Vì vậy, khi tôi cần cùng một đầu ra từ cùng một phương thức như trước, có cách tiếp cận tốt hơn để lưu trữ đầu ra từ trước và biến nó một lần nữa - trong khi giữ nó trong bộ nhớ tất cả thời gian này - hoặc gọi lại cùng một phương thức?Có tốt hơn khi lưu trữ giá trị dưới dạng biến hoặc gọi lại không?

+2

Xem chủ đề ghi nhớ http://en.wikipedia.org/wiki/Memoization. Đó là cái tên ưa thích, học thuật cho những gì bạn mô tả –

Trả lời

10

Thực hành tốt hơn là giữ đầu ra trong một biến thay vì gọi hàm lại. Biến sẽ được giữ trong bộ nhớ miễn là nó đòi hỏi. Sau đó, bộ sưu tập rác tự động sẽ xử lý nó để giải phóng nó khỏi bộ nhớ. Nhưng nếu bạn gọi hàm, nó sẽ ăn bộ nhớ cho ngăn xếp bản ghi kích hoạt của nó mỗi khi nó được gọi. Vì vậy, nếu bạn muốn chương trình của bạn không phải là bộ nhớ đói, nó tốt hơn để lưu trữ kết quả trong một biến và sử dụng nó bất cứ nơi nào nó được yêu cầu.

+0

Và nếu một hàm trả về một đối tượng mới được tạo cho mỗi cuộc gọi có nghĩa là có nhiều công việc hơn cho bộ thu gom rác. –

+1

Ngoài ra, bất kỳ và tất cả các biến trong phương thức sẽ có một bản sao mới của chúng được tạo ra mỗi lần. addNumbers (int x, int y) {return x + y; } sẽ tạo hai biến mới mỗi khi phương thức được gọi (nó sẽ không sử dụng lại các biến cũ, nó sẽ tạo ra các biến mới). Vì vậy, nếu bạn gọi nó hai lần, đó là 4 biến. Nếu bạn gọi nó một lần và lưu trữ câu trả lời, đó là 3 biến. Vì vậy, đã có, lưu trữ nó là sử dụng bộ nhớ ít hơn, và CPU ít hơn vì việc tính toán chỉ cần được thực hiện một lần. – NibblyPig

2

Tạo biến tạm thời không thể là chậm hơn.

Quan trọng hơn, mặc dù: lưu trữ giá trị được trả lại trong một biến làm tăng khả năng đọc và khả năng bảo trì của mã. Ví dụ:

Biz nibb = getBiz(); 
frobnicate(nibb.foo(), nibb.bar()); 
galuncify(nibb.bar()); 

Nếu tôi quyết định rằng giá trị của nibb.biz() nên được sử dụng thay vì nibb.bar(), tôi phải nhớ để thay thế nó trong hai nơi. Vấn đề này sẽ không xảy ra nếu tôi đã sử dụng một biến thay thế:

Biz nibb = getBiz(); 
Bar bar = nibb.bar(); 
frobnicate(nibb.foo(), bar); 
galuncify(bar); 

Cũng chú ý cách thức các cuộc gọi phương thức trở nên dễ đọc hơn.

+0

Tôi không nghĩ câu trả lời này giải quyết được câu hỏi của anh ấy chút nào. Hãy tưởng tượng một loạt các câu lệnh 'if age> 100, if age> 75'. tuổi giả định được truy lục thông qua một phương thức, đang làm 'if guy.getAge()> 100, guy.getAge()> 75' vv. bộ nhớ tốt hơn hoặc tệ hơn/cpuwise hơn là làm 'age = guy.getAge()' trước, và bằng cách đó? – NibblyPig

+0

Câu hỏi là cách tiếp cận nào "thường tốt hơn". Các chu kỳ CPU thường không phải là vấn đề đáng lo ngại trong tình huống này, các câu hỏi về tốc độ không thể được trả lời mà không biết máy và JRE, và thậm chí chỉ có thể được trả lời bằng thử nghiệm, và toàn bộ điều chỉ là trường hợp tối ưu hóa vi mô sớm dù sao. – Thomas

1

Có rất nhiều yếu tố, bao gồm cả sở thích cá nhân, expensiveness của phương pháp, sửa đổi mà phương pháp này không vv

  • Đối với JavaBean cơ bản/bản đồ/danh sách có được phương pháp, tôi thường gọi hai lần cho redability, nhưng đây là sở thích cá nhân. Điều tương tự với phương pháp này là
  • Đối với các hoạt động đắt hơn trả về giá trị (công cụ URL của IE), tốt nhất là lưu trữ vì hoạt động lâu dài để truy cập internet, lấy dữ liệu và báo cáo lại
  • Nếu phương pháp sửa đổi ANYTHING trong khi tìm nạp dữ liệu, tôi lưu trữ giá trị
4

Như tôi hiểu câu hỏi, nó hỏi mà là tốt hơn

Bar bar = foo.getBar(); 
bar.donkey(); 
... 
bar.kong(); 

hoặc

foo.getBar().donkey(); 
... 
foo.getBar().kong(); 

Trừ getBar là đắt tiền, ít có khả năng có sự khác biệt hiệu suất đáng kể. Tương tự như vậy, bộ nhớ rất khó có thể là một vấn đề. Tập trung vào sự hiểu biết.

Có nhiều ý kiến ​​khác nhau.

Tôi tin rằng Martin Fowler thích cái sau. Mã độc lập, và có lẽ dễ dàng hơn để cấu trúc lại.

Tôi có ý kiến ​​rằng việc tái cấu trúc phục vụ mục đích của sự dễ hiểu.Các cựu nói với tôi rằng đối tượng được sử dụng trong cả hai cuộc gọi thực sự có một nguồn chung, và như là một tiền thưởng những gì nó là [loại]. Nói chung rõ ràng hơn và, mặc dù tiết lộ chi tiết hơn, ít xảy ra hơn.

Mặt khác, bạn có thể muốn tắt nó trong một cái gì đó như:

foo.donkey(); 
... 
foo.kong(); 
+0

+1 - trong trường hợp không có bằng chứng cho thấy khả năng * không hiệu quả của việc gọi "getter" hai lần thực sự quan trọng, khả năng đọc và tính đúng đắn nên được coi là quan trọng hơn. Ghi nhớ rằng đối với một getter đơn giản thường có thể được inlined bởi trình biên dịch JIT và trong trường hợp đó chỉ mất 2 hoặc 3 hướng dẫn bản địa. –

+1

Dưới đây là một trích dẫn từ "Tái cấu trúc: Cải thiện Thiết kế Mã hiện tại" của Fowler (trang 21): "Tôi muốn loại bỏ các biến tạm thời như thế này càng nhiều càng tốt. Temps thường là một vấn đề ở chỗ chúng gây ra rất nhiều các thông số được truyền xung quanh khi chúng không cần thiết. Bạn có thể dễ dàng mất dấu những gì chúng đang ở đó. Chúng đặc biệt xảo quyệt trong các phương pháp dài. Nhưng nó rất dễ dàng để tối ưu hóa ... và bạn có thể tối ưu hóa hiệu quả hơn nhiều khi mã được xác định đúng ". –

1

Thật không may câu trả lời là gần như chắc chắn "nó phụ thuộc". Trong hầu hết các trường hợp mà sự cân bằng thời gian/bộ nhớ là quan trọng, bạn sẽ muốn căn cứ hành động của mình trên bằng chứng hơn là trực giác.

Tức là, hãy đo thời gian và hồ sơ bộ nhớ của chương trình của bạn bằng dữ liệu thực tế. Đối với các đoạn mã thậm chí còn tầm thường, rất khó để tìm ra các hiệu ứng tương đối trên nội tuyến, JITing, bộ nhớ cache và vv.

Các vấn đề liên quan