Phân tích sử dụng bộ nhớ: Java vs C ++ Không đáng kể?


9

Làm thế nào để sử dụng bộ nhớ của một đối tượng số nguyên được viết bằng Java so sánh \ tương phản với việc sử dụng bộ nhớ của một đối tượng số nguyên được viết bằng C ++? Là sự khác biệt không đáng kể? Không khác nhau? Một sự khác biệt lớn? Tôi đoán nó giống nhau bởi vì một int là một int bất kể ngôn ngữ (?)

Lý do tại sao tôi hỏi điều này là vì tôi đã đọc về tầm quan trọng của việc biết khi nào yêu cầu bộ nhớ của chương trình sẽ ngăn lập trình viên giải quyết một vấn đề nhất định.

Điều làm tôi thích thú là dung lượng bộ nhớ cần thiết để tạo một đối tượng Java. Lấy ví dụ, một đối tượng số nguyên. Sửa lỗi cho tôi nếu tôi sai nhưng một đối tượng số nguyên Java cần 24 byte bộ nhớ:

  • 4 byte cho biến đối tượng int của nó
  • 16 byte chi phí (tham chiếu đến lớp của đối tượng, thông tin thu thập rác & thông tin đồng bộ hóa)
  • 4 byte đệm

Một ví dụ khác, một mảng Java (được triển khai như một đối tượng) yêu cầu 48 + byte:

  • 24 byte thông tin tiêu đề
  • 16 byte chi phí đối tượng
  • 4 byte cho chiều dài
  • 4 byte cho phần đệm
  • cộng với bộ nhớ cần thiết để lưu trữ các giá trị

Làm thế nào để sử dụng bộ nhớ so sánh với cùng một mã được viết trong C ++?

Tôi đã từng không biết gì về việc sử dụng bộ nhớ của các chương trình C ++ và Java mà tôi đã viết, nhưng bây giờ khi tôi bắt đầu tìm hiểu về các thuật toán, tôi đánh giá cao hơn về tài nguyên của máy tính.


6
"Đối tượng số nguyên" trong C ++ là gì? int? Nếu vậy, bạn nên so sánh điều đó với intJava, chứ không phải Integer- miễn là số nguyên C ++ của bạn là 32 bit.
Mat

+1 Nếu tôi đã tạo một lớp c ++ chỉ có một biến int, thì nó đã khởi tạo nó
Anthony

3
một int không phải là int - nó phụ thuộc vào nền tảng

1
Một C ++ chỉ có một thành viên int thường sẽ không có bất kỳ chi phí nào. Nó sẽ sử dụng chính xác nhiều không gian như nền tảng sử dụng để lưu trữ một int (thường là 4 byte trên các nền tảng PC hiện tại).
Dirk Holsopple

+1 cho một lập trình viên Java đang tò mò về bộ nhớ. Hơn bất cứ điều gì khác, nhận thức về bộ nhớ là yếu tố quan trọng nhất quyết định hiệu suất trên các kiến ​​trúc hiện đại.
imallett

Câu trả lời:


15

Nó phụ thuộc vào nền tảng và thực hiện.

C ++ đảm bảo rằng kích thước của charchính xác là một byte và rộng tối thiểu 8 bit. Sau đó, kích thước của a short intít nhất là 16 bit và không nhỏ hơn char. Kích thước của một intít nhất là lớn bằng kích thước của short int. Kích thước long inttối thiểu là 32 bit và không nhỏ hơn int.

sizeof(char) == 1; sizeof(long int) >= sizeof(int) >= sizeof(short int) >= sizeof(bool) >= sizeof(char).

Mô hình bộ nhớ thực tế của C ++ rất nhỏ gọn và có thể dự đoán được . Ví dụ, không có siêu dữ liệu trong các đối tượng, mảng hoặc con trỏ. Các cấu trúc và các lớp tiếp giáp nhau giống như các mảng, nhưng phần đệm có thể được đặt khi cần thiết và cần thiết.

Thành thật mà nói, việc so sánh như vậy là ngớ ngẩn nhất vì việc sử dụng bộ nhớ Java phụ thuộc nhiều vào việc triển khai Java hơn là mã mà nó chạy.


1
Đúng, nhưng để so sánh, tôi muốn nói rằng chúng ta nên sử dụng các loại số nguyên có kích thước tương đương (ngay cả khi chúng chỉ có sẵn dưới các tên khác nhau). Xét cho cùng, các kích thước khác nhau có nghĩa là ngữ nghĩa khác nhau và trên nhiều nền tảng chung (không phải tất cả), các kích thước giống hệt nhau hoặc các số nguyên có cùng kích thước có sẵn dưới các tên khác nhau.

Lưu ý với OP: Bạn có thể tốt hơn nên chọn cho mình kích thước của số nguyên - nếu bạn muốn int 32 bit trong C ++, bạn có thể sử dụng int32_t.
K.Steff

9

Hầu hết các câu trả lời dường như đang bỏ qua một vài điểm khá quan trọng.

Đầu tiên, trong rất nhiều Java, bạn hầu như không bao giờ thấy một bản thô int- gần như tất cả việc sử dụng là của Integer, vì vậy thực tế là một intkích thước có thể giống như inttrong C hoặc C ++ gần như không liên quan, ngoại trừ điều đó ( theo kinh nghiệm của tôi, nhỏ) phần trăm mã chỉ sử dụng intthay vì Integer.

Thứ hai, kích thước của các đối tượng riêng lẻ hầu như không liên quan gì đến toàn bộ bộ nhớ của một chương trình. Trong Java, dấu chân bộ nhớ của chương trình liên quan chủ yếu đến cách bộ thu gom rác đã được điều chỉnh. Trong hầu hết các trường hợp, GC được điều chỉnh để tối đa hóa tốc độ, điều này (phần lớn) có nghĩa là chạy GC không thường xuyên nhất có thể.

Hiện tại tôi không có một liên kết tiện dụng, nhưng đã có một số thử nghiệm cho thấy Java có thể chạy với tốc độ tương đương với C, nhưng để làm được như vậy, bạn phải chạy GC hiếm khi đủ để nó sử dụng khoảng 7 lần ký ức. Đó không phải là vì các vật thể riêng lẻ lớn gấp 7 lần, mà bởi vì GC có thể trở nên đắt đỏ nếu bạn làm điều đó quá thường xuyên. Tồi tệ hơn, GC chỉ có thể giải phóng bộ nhớ khi nó có thể "chứng minh" rằng không còn cách nào để truy cập vào một đối tượng, thay vì chỉ đơn giản là khi bạn biết bạn đã sử dụng xong nó. Điều này có nghĩa là ngay cả khi bạn chạy GC thường xuyên hơn để giảm thiểu việc sử dụng bộ nhớ, bạn vẫn có thể lập kế hoạch cho một chương trình thông thường có dung lượng bộ nhớ lớn hơn. Trong trường hợp như thế này, bạn có thể giảm hệ số xuống còn 2 hoặc 3 thay vì 7. Ngay cả khi bạn quá nhiệt tình, tuy nhiên, đừng '1 .

Tùy thuộc vào tình huống, có một yếu tố khác có thể có hoặc không đáng kể: bộ nhớ bị chiếm giữ bởi chính JVM. Điều này ít nhiều đã được sửa, vì vậy, tỷ lệ phần trăm có thể rất lớn nếu ứng dụng không cần nhiều dung lượng của riêng nó hoặc có thể rất nhỏ nếu ứng dụng cần lưu trữ nhiều. Ít nhất là trên máy tính của tôi, ngay cả ứng dụng Java tầm thường nhất dường như chiếm khoảng 20-25 megabyte (có thể hơn 1000 lần đối với các chương trình tầm thường hoặc gần như vô cùng nhỏ đối với các ứng dụng lớn).


1 Điều đó không có nghĩa là không ai có thể quản lý để viết Java với dấu chân gần với những gì bạn nhận được với C ++. Chỉ có thể nói rằng chỉ cần có cùng số lượng / kích thước của các đối tượng và việc chạy GC thực sự thường xuyên sẽ không đưa bạn đến đó như một quy luật.


7
Về điểm đầu tiên của bạn: Tôi không phải là người Java, nhưng các API Java mà tôi thấy chưa từng sử dụng Integer(tại sao họ lại như vậy?) Thay vì int. Chỉ các bộ sưu tập chung không có lựa chọn nào khác ngoài việc sử dụng Integerdo xóa kiểu, nhưng nếu bạn quan tâm, bạn có thể thay thế các bộ sưu tập bằng một triển khai chuyên biệt inthoặc bất kỳ loại nguyên thủy nào bạn cần. Và sau đó có quyền anh tạm thời để chuyển qua mã gói chung (ví dụ: mọi thứ cần có Object[]). Ngoài ra, bạn có nguồn cho không gian GC không? Tôi không thực sự nghi ngờ điều đó, tôi chỉ tò mò.


9

Tôi hy vọng bạn nhận ra rằng tất cả những điều này được định nghĩa triển khai sâu sắc, cả cho Java và C ++. Điều đó đang được nói, mô hình đối tượng của Java đòi hỏi khá nhiều không gian.

Các đối tượng C ++ không (nói chung) không cần bất kỳ lưu trữ nào ngoại trừ những gì các thành viên cần. Lưu ý rằng (không giống như Java, trong đó mọi thứ do người dùng định nghĩa là loại tham chiếu), mã máy khách có thể sử dụng một đối tượng làm kiểu giá trị hoặc làm kiểu tham chiếu, tức là một đối tượng có thể lưu trữ con trỏ / tham chiếu đến đối tượng khác hoặc lưu trữ đối tượng trực tiếp không có sự quyết định Một con trỏ bổ sung cho mỗi đối tượng là cần thiết nếu có bất kỳ virtualphương thức nào , nhưng khá nhiều lớp hữu ích được thiết kế để hòa hợp mà không cần đa hình và không cần điều này. Không có siêu dữ liệu GC và không có khóa trên mỗi đối tượng. Do đó, class IntWrapper { int x; public: IntWrapper(int); ... };các đối tượng không cần nhiều không gian hơn ints đơn giản và có thể được đặt trực tiếp (tức là không có hướng dẫn) trong các bộ sưu tập và các đối tượng khác.

Mảng rất đơn giản vì không có tiền chế tạo, tương đương với Mảng Java trong C ++. Bạn chỉ có thể phân bổ một loạt các đối tượng với new[](hoàn toàn không có chi phí / siêu dữ liệu) nhưng không có trường độ dài - việc triển khai có thể lưu trữ một đối tượng nhưng bạn không thể truy cập nó. std::vectorlà một mảng động và do đó có thêm chi phí và giao diện lớn hơn. std::arrayvà mảng kiểu C (int arr[N];), cần một hằng số thời gian biên dịch. Về lý thuyết, nó chỉ nên là bộ lưu trữ của đối tượng cộng với một số nguyên duy nhất cho chiều dài - nhưng vì bạn có thể thay đổi kích thước động và giao diện đầy đủ tính năng với rất ít không gian, nên bạn chỉ cần thực hiện điều đó trong thực tế. Lưu ý rằng tất cả những thứ này, cũng như tất cả các bộ sưu tập khác, mặc định lưu trữ các đối tượng theo giá trị, do đó giúp bạn tiết kiệm được không gian và không gian cho các tài liệu tham khảo và cải thiện hành vi bộ đệm. Bạn phải lưu trữ rõ ràng con trỏ (những người thông minh, xin vui lòng) để có được sự quyết định.

Các so sánh trên không hoàn toàn công bằng, vì một số khoản tiết kiệm này được cung cấp bởi không bao gồm các tính năng Java bao gồm và tương đương C ++ của chúng thường ít được tối ưu hóa hơn so với tương đương Java (*). Cách phổ biến để thực hiện virtualtrong C ++ áp đặt chính xác nhiều chi phí như cách phổ biến để thực hiện virtualtrong Java. Để có được một khóa, bạn cần một đối tượng mutex có đầy đủ tính năng, rất có thể lớn hơn một vài bit. Để có được số tham chiếu ( không phảitương đương với GC và không nên được sử dụng như vậy, nhưng đôi khi hữu ích), bạn cần một con trỏ thông minh có thêm trường đếm tham chiếu. Trừ khi đối tượng được xây dựng cẩn thận, số tham chiếu, đối tượng con trỏ thông minh và đối tượng được tham chiếu nằm ở các vị trí hoàn toàn riêng biệt và ngay cả khi bạn xây dựng đúng, con trỏ dùng chung có thể (phải?) Vẫn là hai con trỏ thay vì một. Một lần nữa, phong cách C ++ tốt không sử dụng các tính năng này đủ để nó trở thành vấn đề - trong thực tế, các đối tượng của thư viện C ++ được viết tốt sử dụng ít hơn. Điều đó không nhất thiết có nghĩa là sử dụng ít bộ nhớ hơn, nhưng điều đó có nghĩa là C ++ có một khởi đầu tốt trong vấn đề này.

(*) Chẳng hạn, bạn có thể nhận các cuộc gọi ảo, mã băm nhận dạng và khóa chỉ bằng một từ cho một số đối tượng (và hai từ cho nhiều đối tượng khác) bằng cách hợp nhất thông tin loại với các cờ khác nhau và xóa bit khóa cho các đối tượng không cần khóa. Xem Triển khai mô hình đối tượng Java (PDF) không gian và thời gian hiệu quả của David F. Bacon, Stephen J. Fink và David Grove để được giải thích chi tiết về điều này và các tối ưu hóa khác.


3

Một đồng bằng int, trong java, chiếm không gian chính xác như inttrong C ++, với điều kiện là cả hai triển khai đều sử dụng cùng kích thước số nguyên và căn chỉnh bộ nhớ.

Một đối tượng int (một số nguyên được đóng hộp , nghĩa là một thể hiện của lớp Integer), mang tất cả chi phí hoạt động của một cá thể lớp trong Java, vì vậy nó lớn hơn đáng kể so với intC ++. Tuy nhiên, nếu bạn trang bị một đối tượng trong C ++ với các tính năng tương tự như các đối tượng Java đi kèm với tính năng đa hình (đa hình, quyền anh, bộ sưu tập rác, RTTI), thì có lẽ bạn sẽ kết thúc với một đối tượng bằng nhau kích thước.

Và sau đó là những cân nhắc tối ưu hóa; do các mô hình thực thi và mô hình lập trình khác nhau, không có khả năng bất kỳ vấn đề không tầm thường nào sẽ được giải quyết giống nhau ở cả hai ngôn ngữ, vì vậy so sánh kích thước lưu trữ ở cấp độ này không có ý nghĩa đáng kinh ngạc.

Đúng, các đối tượng Java mang nhiều chi phí mặc định hơn các lớp C ++, nhưng chúng có nhiều tính năng hơn và điều này dẫn đến một phong cách lập trình khác - một lập trình viên giỏi có thể tận dụng những nhược điểm của ngôn ngữ.


+1 Nhiều chi phí hơn nhưng nhiều tính năng hơn trong Java, tôi hiểu ngay bây giờ, cảm ơn
Anthony
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.