Phân bổ vùng heap Java nhanh hơn C ++


13

Tôi đã đăng câu hỏi này lên SO và nó đã ổn. Nó không may bị đóng mặc dù (chỉ cần một phiếu để mở lại) nhưng ai đó đề nghị tôi đăng nó lên đây vì nó phù hợp hơn nên sau đây đúng là một bản sao của câu hỏi


Tôi đã đọc những bình luận về câu trả lời này và tôi đã thấy trích dẫn này.

Tính năng khởi tạo đối tượng và hướng đối tượng được sử dụng rất nhanh (nhanh hơn C ++ trong nhiều trường hợp) vì chúng được thiết kế ngay từ đầu. và Bộ sưu tập nhanh chóng. Java tiêu chuẩn đánh bại C / C ++ tiêu chuẩn trong lĩnh vực này, ngay cả đối với hầu hết mã C được tối ưu hóa.

Một người dùng (có đại diện thực sự cao tôi có thể thêm) mạnh dạn bảo vệ khiếu nại này, nói rằng

  1. phân bổ heap trong java tốt hơn so với C ++

  2. và thêm tuyên bố này bảo vệ các bộ sưu tập trong java

    Và các bộ sưu tập Java nhanh so với các bộ sưu tập C ++ do phần lớn là do hệ thống con bộ nhớ khác nhau.

Vì vậy, câu hỏi của tôi là bất kỳ điều này có thể thực sự đúng không, và nếu vậy tại sao phân bổ heap của java lại nhanh hơn nhiều.


Bạn có thể tìm thấy câu trả lời của tôi cho một câu hỏi tương tự trên SO hữu ích / có liên quan.
Daniel Pryden

1
Thật tầm thường: với Java (hoặc bất kỳ môi trường bị hạn chế, được quản lý nào khác), bạn có thể di chuyển các đối tượng và cập nhật các con trỏ tới chúng - tức là, tối ưu hóa một cách linh hoạt cho một địa phương bộ đệm tốt hơn. Với C ++ và các tính năng con trỏ của nó với các bitcast không được kiểm soát, tất cả các đối tượng được ghim vào vị trí của chúng mãi mãi.
SK-logic

3
Tôi chưa bao giờ nghĩ rằng tôi nghe ai đó nói rằng quản lý bộ nhớ Java nhanh hơn bởi vì nó sao chép bộ nhớ mọi lúc. thở dài.
gbjbaanb

1
@gbjbaanb, bạn đã bao giờ nghe nói về hệ thống phân cấp bộ nhớ chưa? Cache bỏ lỡ hình phạt? Bạn có nhận ra rằng một phân bổ mục đích chung là đắt tiền, trong khi phân bổ thế hệ đầu tiên chỉ là một hoạt động bổ sung duy nhất?
SK-logic

1
Mặc dù điều này có thể đúng một chút trong một số trường hợp, nhưng nó bỏ lỡ điểm trong java bạn phân bổ mọi thứ trên heap và trong c ++, bạn phân bổ rất nhiều đối tượng trên ngăn xếp có thể nhanh hơn rất nhiều.
JohnB

Câu trả lời:


23

Đây là một câu hỏi thú vị, và câu trả lời là phức tạp.

Nhìn chung, tôi nghĩ thật công bằng khi nói rằng trình thu gom rác JVM được thiết kế rất tốt và cực kỳ hiệu quả. Đây có lẽ là hệ thống quản lý bộ nhớ mục đích chung tốt nhất .

C ++ có thể đánh bại JVM GC bằng các bộ cấp phát bộ nhớ chuyên dụng được thiết kế cho các mục đích cụ thể. Ví dụ có thể là:

  • Bộ cấp phát bộ nhớ cho mỗi khung, giúp quét toàn bộ vùng nhớ theo các khoảng thời gian định kỳ. Chúng thường được sử dụng trong các trò chơi C ++, ví dụ, trong đó một vùng bộ nhớ tạm thời được sử dụng một lần trên mỗi khung hình và ngay lập tức bị loại bỏ.
  • Phân bổ tùy chỉnh quản lý một nhóm các đối tượng có kích thước cố định
  • Phân bổ dựa trên ngăn xếp (mặc dù lưu ý rằng JVM cũng thực hiện điều này trong các trường hợp khác nhau, ví dụ như thông qua phân tích thoát )

Tất nhiên, cấp phát bộ nhớ chuyên biệt, bị giới hạn bởi định nghĩa. Chúng thường có các hạn chế đối với vòng đời của đối tượng và / hoặc các hạn chế đối với loại đối tượng có thể được quản lý. Thu gom rác linh hoạt hơn nhiều.

Bộ sưu tập rác cũng cung cấp cho bạn một số lợi thế đáng kể từ góc độ hiệu suất:

  • Đối tượng khởi tạo thực sự là cực kỳ nhanh chóng. Do cách các đối tượng mới được phân bổ tuần tự trong bộ nhớ, nên nó thường yêu cầu ít hơn một bổ sung con trỏ, điều này chắc chắn nhanh hơn các thuật toán phân bổ heap C ++ điển hình.
  • Bạn tránh được nhu cầu về chi phí quản lý vòng đời - ví dụ: đếm tham chiếu (đôi khi được sử dụng thay thế cho GC) rất kém từ góc độ hiệu suất do việc tăng và giảm số lượng tham chiếu thường xuyên làm tăng thêm chi phí hiệu năng (thường nhiều hơn so với GC) .
  • Nếu bạn sử dụng các đối tượng không thay đổi, bạn có thể tận dụng chia sẻ cấu trúc để tiết kiệm bộ nhớ và cải thiện hiệu quả bộ nhớ cache. Điều này được sử dụng nhiều bởi các ngôn ngữ chức năng trên JVM như Scala và Clojure. Rất khó để làm điều này nếu không có GC, bởi vì cực kỳ khó để quản lý vòng đời của các đối tượng được chia sẻ. Nếu bạn tin (như tôi làm) rằng tính bất biến và chia sẻ cấu trúc là chìa khóa để xây dựng các ứng dụng đồng thời lớn, thì đây có thể coi là lợi thế hiệu suất lớn nhất của GC.
  • Bạn có thể tránh sao chép nếu tất cả các loại đối tượng và vòng đời tương ứng của chúng được quản lý bởi cùng một hệ thống thu gom rác. Tương phản với C ++, nơi bạn thường phải lấy các bản sao đầy đủ của dữ liệu vì đích đến đòi hỏi một cách tiếp cận quản lý bộ nhớ khác hoặc có vòng đời đối tượng khác.

Java GC có một nhược điểm lớn: bởi vì công việc thu gom rác được hoãn lại và được thực hiện trong các khối công việc theo chu kỳ, nó gây ra tạm dừng GC để thu gom rác, có thể ảnh hưởng đến độ trễ. Đây thường không phải là vấn đề đối với các ứng dụng thông thường, nhưng có thể loại trừ Java trong các tình huống trong đó thời gian thực cứng là một yêu cầu (ví dụ: điều khiển robot). Thời gian thực mềm (ví dụ: trò chơi, đa phương tiện) thường là OK.


có các thư viện chuyên ngành trong khu vực c ++ giải quyết vấn đề đó. Ví dụ nổi tiếng nhất có lẽ là SmartHeap.
Tobias Langner

5
Thời gian thực mềm không có nghĩa là bạn thường dừng lại . Nó chỉ có nghĩa là bạn có thể tạm dừng / thử lại trong tình huống xấu thực sự - thường là bất ngờ - thay vì tạm dừng / sụp đổ / thất bại. Không ai muốn sử dụng thường tạm dừng máy nghe nhạc. Vấn đề của tạm dừng GC là nó xảy ra thường xuyênkhông thể đoán trước . Theo cách đó, tạm dừng GC không được chấp nhận ngay cả đối với ứng dụng thời gian thực mềm. Tạm dừng GC chỉ được chấp nhận khi người dùng không quan tâm đến chất lượng ứng dụng. Và ngày nay, mọi người không còn ngây thơ nữa.
Eonil

1
Vui lòng đăng một số phép đo hiệu suất để hỗ trợ cho tuyên bố của bạn, nếu không, chúng tôi sẽ so sánh táo và cam.
JBRWilkinson

1
@Demetri Nhưng trong thực tế, chỉ khi trường hợp xảy ra quá nhiều (và một lần nữa, thậm chí không thể đoán trước!) Trừ khi bạn có thể đáp ứng một số ràng buộc không thực tế. Nói cách khác, C ++ dễ dàng hơn nhiều cho mọi tình huống thời gian thực.
Eonil

1
Để hoàn thiện: có một nhược điểm khác của hiệu năng GC: như trong hầu hết các bộ nhớ giải phóng GC hiện có xảy ra trong một luồng khác có khả năng chạy trên lõi khác, điều đó có nghĩa là các GC đang phải chịu chi phí vô hiệu hóa bộ đệm nghiêm trọng để đồng bộ hóa L1 / L2 lưu trữ giữa các lõi khác nhau; hơn nữa, trên các máy chủ chủ yếu là NUMA, bộ đệm L3 cũng phải được đồng bộ hóa (và trên Hypertransport / QPI, ouch (!)).
No-Bugs Hare

3

Đây không phải là một tuyên bố khoa học. Tôi chỉ đơn giản là đưa ra một số thực phẩm để suy nghĩ về vấn đề này.

Một điểm tương đồng trực quan là thế này: bạn được cấp một căn hộ (một đơn vị ở) được trải thảm. Thảm bẩn. Cách nhanh nhất (tính theo giờ) để làm cho sàn của căn hộ lấp lánh sạch sẽ là gì?

Trả lời: chỉ cần cuộn tấm thảm cũ lên; vứt đi; và lăn ra một tấm thảm mới.

Chúng ta đang bỏ bê điều gì ở đây?

  • Chi phí di chuyển đồ đạc cá nhân hiện có và sau đó chuyển vào.
    • Đây được gọi là chi phí thu gom rác "dừng lại trên thế giới".
  • Chi phí của thảm mới.
    • Mà, trùng hợp với RAM, nó là miễn phí.

Bộ sưu tập rác là một chủ đề lớn và có rất nhiều câu hỏi cả trong Lập trình viên.SE và StackOverflow.

Về một vấn đề phụ, trình quản lý phân bổ C / C ++ được gọi là TCMalloc cùng với việc đếm tham chiếu đối tượng về mặt lý thuyết có thể đáp ứng các yêu cầu hiệu suất tốt nhất của bất kỳ hệ thống GC nào.


Trên thực tế, c ++ 11 thậm chí còn có bộ sưu tập rác ABI , điều này khá giống với một số câu trả lời tôi nhận được trên SO
aaronman

Chính nỗi sợ phá vỡ các chương trình C / C ++ hiện tại (cơ sở mã, chẳng hạn như hạt nhân Linux và các thư viện arch cổ_but_still_economical_important như libtiff) đã cản trở tiến trình đổi mới ngôn ngữ trong C ++.
rwong

Có nghĩa là, tôi đoán bằng c ++ 17 nó sẽ hoàn thiện hơn, nhưng sự thật là một khi bạn thực sự học cách lập trình trong c ++, bạn thậm chí không muốn nó nữa, có lẽ họ có thể tìm cách kết hợp hai thành ngữ này độc đáo
aaronman

Bạn có nhận ra rằng có những người thu gom rác không ngăn được thế giới không? Bạn đã xem xét ý nghĩa hiệu suất của việc nén (về phía GC) và phân mảnh heap (đối với các cấp phát C ++ chung) chưa?
SK-logic

2
Tôi nghĩ lỗ hổng chính trong sự tương tự này là những gì GC thực sự làm là tìm các bit bẩn, cắt chúng ra và sau đó thấy các bit còn lại trở lại với nhau để tạo ra một tấm thảm mới.
Svick

3

Lý do chính là, khi bạn yêu cầu Java cho một khối bộ nhớ mới, nó sẽ đi thẳng đến cuối đống và cung cấp cho bạn một khối. Theo cách này, phân bổ bộ nhớ nhanh như phân bổ trên ngăn xếp (đó là cách bạn làm điều đó hầu hết thời gian trong C / C ++, nhưng ngoài điều đó ..)

Vì vậy, phân bổ nhanh như mọi thứ nhưng ... điều đó không tính chi phí giải phóng bộ nhớ. Chỉ vì bạn không miễn phí bất cứ điều gì cho đến sau này không có nghĩa là nó không tốn kém khá nhiều, và trong trường hợp của hệ thống GC, chi phí cao hơn rất nhiều so với phân bổ heap 'bình thường' - không chỉ GC phải chạy qua tất cả các đối tượng để xem chúng còn sống hay không, sau đó nó cũng phải giải phóng chúng và (chi phí lớn) sao chép bộ nhớ xung quanh để thu gọn heap - để cuối cùng bạn có thể phân bổ nhanh cơ chế (hoặc bạn sẽ hết bộ nhớ, ví dụ C / C ++ sẽ đi theo đống trên mỗi phân bổ tìm kiếm khối không gian trống tiếp theo có thể phù hợp với đối tượng).

Đây là một lý do tại sao các điểm chuẩn Java / .NET cho thấy hiệu năng tốt như vậy, nhưng các ứng dụng trong thế giới thực cho thấy hiệu năng kém như vậy. Tôi chỉ cần nhìn vào các ứng dụng trên điện thoại của tôi - thực sự nhanh chóng, những người đáp ứng được tất cả các văn bản bằng cách sử dụng NDK, nhiều đến nỗi thậm chí tôi đã rất ngạc nhiên.

Bộ sưu tập ngày nay có thể nhanh nếu tất cả các đối tượng được phân bổ cục bộ, ví dụ trong một khối liền kề duy nhất. Bây giờ, trong Java, bạn chỉ đơn giản là không nhận được các khối liền kề nhau khi các đối tượng được phân bổ từng khối một từ đầu cuối miễn phí. Bạn có thể kết thúc với chúng một cách vui vẻ liền kề nhau, nhưng chỉ bằng may mắn (tức là theo ý thích của thói quen nén GC và cách nó sao chép các đối tượng). Mặt khác, C / C ++ hỗ trợ rõ ràng việc phân bổ liền kề (rõ ràng thông qua ngăn xếp). Nói chung các đối tượng heap trong C / C ++ không khác với BTW của Java.

Giờ đây với C / C ++, bạn có thể nhận được tốt hơn các bộ cấp phát mặc định được thiết kế để tiết kiệm bộ nhớ và sử dụng hiệu quả. Bạn có thể thay thế bộ cấp phát bằng một nhóm các nhóm khối cố định, vì vậy bạn luôn có thể tìm thấy một khối có kích thước chính xác cho đối tượng bạn đang phân bổ. Đi bộ heap chỉ là vấn đề tìm kiếm bitmap để xem khối miễn phí ở đâu và phân bổ đơn giản chỉ là thiết lập lại một chút trong bitmap đó. Chi phí là bạn sử dụng nhiều bộ nhớ hơn khi bạn phân bổ trong các khối có kích thước cố định, do đó bạn có một đống 4 khối, một khối khác cho các khối 16 byte, v.v.


2
Có vẻ như bạn không hiểu gì về GC cả. Hãy xem xét kịch bản điển hình nhất - hàng trăm đối tượng nhỏ được phân bổ liên tục, nhưng chỉ một tá trong số chúng sẽ tồn tại trong hơn một giây. Bằng cách này, hoàn toàn không có chi phí trong việc giải phóng bộ nhớ - hàng tá này được sao chép từ thế hệ trẻ (và được thu gọn, như một lợi ích bổ sung), và phần còn lại được loại bỏ miễn phí. Và, nhân tiện, Dalvik GC thảm hại không liên quan gì đến các GC hiện đại, hiện đại mà bạn sẽ tìm thấy trong các triển khai JVM thích hợp.
SK-logic

1
Nếu một trong những vật thể được giải phóng đó ở giữa đống, phần còn lại của đống sẽ được nén lại để lấy lại không gian. Hay bạn đang nói rằng việc nén GC không xảy ra trừ khi đó là trường hợp tốt nhất bạn mô tả? Tôi biết các GC thế hệ làm tốt hơn rất nhiều ở đây, trừ khi bạn giải phóng một đối tượng ở giữa các thế hệ sau, trong trường hợp đó tác động có thể tương đối lớn. Có một cái gì đó được viết bởi một Microsoftie, người đã làm việc trên GC của họ mà tôi đã đọc mô tả sự đánh đổi của GC khi tạo ra một thế hệ GC .. Tôi sẽ xem liệu tôi có thể tìm lại được không.
gbjbaanb

1
Bạn đang nói về "đống" nào? Hầu hết rác được thu hồi ở giai đoạn thế hệ trẻ, và hầu hết các lợi ích về hiệu suất đều đến chính xác từ sự nén gọn đó. Tất nhiên, nó hầu như hiển thị trên hồ sơ cấp phát bộ nhớ điển hình cho lập trình chức năng (nhiều đối tượng nhỏ sống ngắn). Và, tất nhiên, có rất nhiều cơ hội tối ưu hóa chưa được khám phá - ví dụ, phân tích vùng động có thể biến phân bổ heap trong một đường dẫn nhất định thành phân bổ stack hoặc pool.
SK-logic

3
Tôi không đồng ý với tuyên bố của bạn rằng phân bổ heap là 'nhanh như stack' - phân bổ heap yêu cầu đồng bộ hóa luồng và stack không (theo định nghĩa)
JBRWilkinson

1
Tôi đoán vậy, nhưng với Java và .net, bạn thấy quan điểm của tôi - bạn không cần phải đi bộ để tìm khối miễn phí tiếp theo để nhanh hơn đáng kể về vấn đề đó, nhưng đúng vậy - bạn đã đúng, nó phải như vậy bị khóa sẽ làm tổn thương các ứng dụng luồng.
gbjbaanb

2

Không gian địa đàng

Vì vậy, câu hỏi của tôi là bất kỳ điều này có thể thực sự đúng không, và nếu vậy tại sao phân bổ heap của java lại nhanh hơn nhiều.

Tôi đã nghiên cứu một chút về cách thức hoạt động của Java GC vì nó rất thú vị đối với tôi. Tôi luôn cố gắng mở rộng bộ sưu tập các chiến lược phân bổ bộ nhớ của mình trong C và C ++ (quan tâm đến việc cố gắng thực hiện một cái gì đó tương tự trong C), và đó là một cách rất, rất nhanh để phân bổ nhiều đối tượng theo kiểu bùng nổ từ quan điểm thực tế nhưng chủ yếu là do đa luồng.

Cách phân bổ Java GC hoạt động là sử dụng chiến lược phân bổ cực kỳ rẻ để ban đầu phân bổ các đối tượng vào không gian "Eden". Từ những gì tôi có thể nói, đó là sử dụng bộ phân bổ nhóm tuần tự.

Điều đó nhanh hơn rất nhiều về mặt thuật toán và giảm các lỗi trang bắt buộc so với mục đích chung malloctrong C hoặc mặc định, ném operator newvào C ++.

Nhưng các bộ cấp phát tuần tự có một điểm yếu rõ ràng: chúng có thể phân bổ các khối có kích thước thay đổi, nhưng chúng không thể giải phóng bất kỳ khối riêng lẻ nào. Họ chỉ phân bổ theo kiểu tuần tự thẳng với phần đệm để căn chỉnh và chỉ có thể lọc tất cả bộ nhớ mà họ đã phân bổ cùng một lúc. Chúng thường hữu ích trong C và C ++ để xây dựng cấu trúc dữ liệu chỉ cần chèn và không loại bỏ các phần tử, như cây tìm kiếm chỉ cần được xây dựng một lần khi chương trình bắt đầu và sau đó được tìm kiếm nhiều lần hoặc chỉ thêm khóa mới ( không có phím nào bị xóa).

Chúng cũng có thể được sử dụng ngay cả đối với các cấu trúc dữ liệu cho phép loại bỏ các phần tử, nhưng các phần tử đó thực sự sẽ không được giải phóng khỏi bộ nhớ vì chúng ta không thể phân bổ chúng riêng lẻ. Cấu trúc như vậy sử dụng bộ cấp phát tuần tự sẽ chỉ tiêu tốn nhiều bộ nhớ hơn, trừ khi nó có một số bị hoãn trong đó dữ liệu được sao chép sang một bản sao mới, được nén bằng cách sử dụng một bộ cấp phát tuần tự riêng biệt (và đôi khi đó là một kỹ thuật rất hiệu quả nếu bộ phân bổ cố định giành chiến thắng Vì lý do nào đó - chỉ cần thẳng lên phân bổ tuần tự một bản sao mới của cấu trúc dữ liệu và kết xuất tất cả bộ nhớ của cái cũ).

Bộ sưu tập

Như trong ví dụ về cấu trúc dữ liệu / nhóm tuần tự ở trên, sẽ là một vấn đề lớn nếu Java GC chỉ phân bổ theo cách này mặc dù nó cực nhanh để phân bổ nhiều khối riêng lẻ. Nó sẽ không thể giải phóng bất cứ thứ gì cho đến khi phần mềm bị tắt, tại thời điểm đó, nó có thể giải phóng tất cả các nhóm bộ nhớ cùng một lúc.

Vì vậy, thay vào đó, sau một chu trình GC duy nhất, một đường chuyền được thực hiện thông qua các đối tượng hiện có trong không gian "Eden" (được phân bổ tuần tự) và những đối tượng vẫn được tham chiếu sau đó được phân bổ bằng cách sử dụng một cấp phát đa mục đích có khả năng giải phóng các khối riêng lẻ. Những người không còn được tham chiếu sẽ chỉ đơn giản là bị xử lý trong quá trình thanh trừng. Vì vậy, về cơ bản, đó là "sao chép các đối tượng ra khỏi không gian Eden nếu chúng vẫn được tham chiếu và sau đó thanh lọc".

Điều này thường sẽ khá tốn kém, vì vậy nó được thực hiện trong một luồng nền riêng biệt để tránh làm chậm đáng kể các luồng ban đầu được phân bổ tất cả bộ nhớ.

Khi bộ nhớ được sao chép ra khỏi không gian Eden và được phân bổ bằng cách sử dụng lược đồ đắt tiền hơn này có thể giải phóng các khối riêng lẻ sau một chu kỳ GC ban đầu, các đối tượng sẽ chuyển sang vùng nhớ liên tục hơn. Các khối riêng lẻ đó sau đó được giải phóng trong các chu kỳ GC tiếp theo nếu chúng không còn được tham chiếu.

Tốc độ

Vì vậy, nói một cách thô thiển, lý do Java GC rất có thể vượt trội so với C hoặc C ++ khi phân bổ heap thẳng là vì nó sử dụng chiến lược phân bổ hoàn toàn thoái hóa, rẻ nhất trong luồng yêu cầu phân bổ bộ nhớ. Sau đó, nó tiết kiệm công việc đắt tiền hơn mà chúng ta thường cần phải làm khi sử dụng một công cụ phân bổ tổng quát hơn như thẳng malloccho một luồng khác.

Vì vậy, về mặt khái niệm, GC thực sự phải thực hiện nhiều công việc tổng thể hơn, nhưng nó phân phối nó qua các luồng để toàn bộ chi phí không được trả trước bởi một luồng. Nó cho phép luồng phân bổ bộ nhớ thực hiện nó với giá siêu rẻ, và sau đó hoãn lại chi phí thực sự cần thiết để thực hiện mọi thứ một cách hợp lý để các đối tượng riêng lẻ thực sự có thể được giải phóng sang luồng khác. Trong C hoặc C ++ khi chúng tôi mallochoặc gọi operator new, chúng tôi phải trả toàn bộ chi phí trả trước trong cùng một chuỗi.

Đây là sự khác biệt chính và tại sao Java rất có thể vượt trội so với C hoặc C ++ khi chỉ sử dụng các cuộc gọi ngây thơ đến mallochoặc operator newphân bổ một nhóm các khối tuổi teen riêng lẻ. Tất nhiên, thông thường sẽ có một số hoạt động nguyên tử và một số khóa tiềm năng khi chu trình GC khởi động, nhưng có lẽ nó được tối ưu hóa khá nhiều.

Về cơ bản, lời giải thích đơn giản tập trung vào việc trả chi phí nặng hơn trong một luồng ( malloc) so với trả chi phí rẻ hơn trong một luồng và sau đó trả chi phí nặng hơn trong một luồng khác có thể chạy song song ( GC). Như một nhược điểm khi thực hiện theo cách này ngụ ý rằng bạn yêu cầu hai lần chuyển hướng từ tham chiếu đối tượng sang đối tượng theo yêu cầu để cho phép người cấp phát sao chép / di chuyển bộ nhớ xung quanh mà không làm mất hiệu lực các tham chiếu đối tượng hiện có và bạn cũng có thể mất vị trí không gian khi bộ nhớ đối tượng là di chuyển ra khỏi không gian "Eden".

Cuối cùng nhưng không kém phần quan trọng, việc so sánh là một chút không công bằng vì mã C ++ thường không phân bổ một lượng thuyền các đối tượng riêng lẻ trên heap. Mã C ++ Decent có xu hướng phân bổ bộ nhớ cho nhiều phần tử trong các khối liền kề hoặc trên ngăn xếp. Nếu nó phân bổ một lượng thuyền của các vật thể nhỏ một lần trên cửa hàng miễn phí, mã sẽ rất tệ.


0

Tất cả phụ thuộc vào người đo tốc độ, tốc độ thực hiện mà họ đo lường và những gì họ muốn chứng minh. Và những gì họ so sánh.

Nếu bạn chỉ nhìn vào việc phân bổ / giải quyết, trong C ++, bạn có thể có 1.000.000 cuộc gọi đến malloc và 1.000.000 cuộc gọi đến miễn phí (). Trong Java, bạn sẽ có 1.000.000 cuộc gọi đến new () và trình thu gom rác đang chạy trong một vòng lặp tìm 1.000.000 đối tượng mà nó có thể giải phóng. Vòng lặp có thể nhanh hơn cuộc gọi free ().

Mặt khác, malloc / free đã cải thiện thời gian khác và thông thường malloc / free chỉ đặt một bit trong cấu trúc dữ liệu riêng biệt và được tối ưu hóa cho malloc / free xảy ra trong cùng một luồng, vì vậy trong môi trường đa luồng không có biến bộ nhớ chia sẻ được sử dụng trong nhiều trường hợp (và các biến bộ nhớ khóa hoặc chia sẻ rất tốn kém).

Mặt khác, có những thứ như đếm tham chiếu mà bạn có thể cần mà không cần thu gom rác và điều đó không miễn phí.

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.