Sự khác biệt giữa VirtualAlloc và HeapAlloc là gì?


81

Có rất nhiều phương pháp để cấp phát bộ nhớ trong môi trường Windows, chẳng hạn như VirtualAlloc, HeapAlloc, malloc, new.

Vì vậy, sự khác biệt giữa chúng là gì?

Câu trả lời:


82

Mỗi API dành cho các mục đích sử dụng khác nhau. Mỗi cái cũng yêu cầu bạn sử dụng đúng chức năng phân bổ / giải phóng thỏa thuận khi bạn sử dụng xong bộ nhớ.

VirtualAlloc

API Windows cấp thấp cung cấp nhiều tùy chọn, nhưng chủ yếu hữu ích cho mọi người trong các tình huống khá cụ thể. Chỉ có thể cấp phát bộ nhớ trong (chỉnh sửa: không phải 4KB) phần lớn hơn. Có những trường hợp bạn cần đến nó, nhưng bạn sẽ biết khi mình ở một trong những tình huống này. Một trong những điều phổ biến nhất là nếu bạn phải chia sẻ bộ nhớ trực tiếp với một quy trình khác. Không sử dụng nó để cấp phát bộ nhớ cho mục đích chung. Sử dụng VirtualFreeđể phân bổ.

HeapAlloc

Phân bổ bất kỳ dung lượng bộ nhớ nào bạn yêu cầu, không phân bổ theo phần lớn hơn VirtualAlloc. HeapAllocbiết khi nào nó cần gọi VirtualAllocvà tự động làm như vậy cho bạn. Giống như malloc, nhưng chỉ dành cho Windows và cung cấp thêm một số tùy chọn. Thích hợp để phân bổ các khối bộ nhớ chung. Một số API Windows có thể yêu cầu bạn sử dụng điều này để phân bổ bộ nhớ mà bạn chuyển cho chúng hoặc sử dụng đồng hành của nó HeapFreeđể giải phóng bộ nhớ mà chúng trả lại cho bạn.

malloc

Cách C. phân bổ bộ nhớ. Ưu tiên điều này nếu bạn đang viết bằng C hơn là C ++ và bạn muốn mã của mình cũng hoạt động trên các máy tính Unix, ví dụ như ai đó nói cụ thể rằng bạn cần sử dụng nó. Không khởi tạo bộ nhớ. Thích hợp để phân bổ các khối bộ nhớ chung, như HeapAlloc. Một API đơn giản. Sử dụng freeđể phân bổ. Visual C ++ 's malloccuộc gọiHeapAlloc .

Mới

Cách cấp phát bộ nhớ của C ++. Ưu tiên điều này nếu bạn đang viết bằng C ++. Nó cũng đưa một đối tượng hoặc các đối tượng vào bộ nhớ được cấp phát. Sử dụng deleteđể phân bổ (hoặc delete[]cho mảng). Các newcuộc gọi của Visual studio HeapAlloc, và sau đó có thể khởi tạo các đối tượng, tùy thuộc vào cách bạn gọi nó.

Trong các tiêu chuẩn C ++ gần đây (C ++ 11 trở lên), nếu bạn phải sử dụng thủ công delete, bạn đang làm sai và nên sử dụng một con trỏ thông minh như unique_ptrthế. Từ C ++ 14 trở đi, có thể nói tương tự new(được thay thế bằng các hàm chẳng hạn make_unique()).


Ngoài ra còn có một số chức năng tương tự khác SysAllocStringmà bạn có thể được thông báo rằng bạn phải sử dụng trong các trường hợp cụ thể.


18
Doug: VirtualAlloc không giới hạn nghiêm ngặt đối với phân bổ 4kb, đó là kích thước được trả về bởi GetSystemInfo (), SYSTEM_INFO :: dwAllocationGranularity. Nó thực sự RARELY 4kb. Trên máy chủ của tôi, nó là 64k, tôi nghi ngờ bạn cũng vậy. 4KB là mục nhập kích thước trang tối thiểu cho các bảng ký hiệu khác nhau trong ABI x86. 4KB là kích thước nhỏ nhất có thể được cấp phép độc lập, R / W / X, tuy nhiên nó không có ý nghĩa gì đối với VirtualAlloc. Nếu bạn tham khảo tài liệu VirtualAlloc, thì cũng có tùy chọn LARGE_PAGES (xem msdn.microsoft.com/en-us/library/aa366568(VS.85).aspx ).
RandomNickName42

bạn có biết tại sao DirectShow sử dụng VirtualAlloc để cấp phát bộ đệm bộ nhớ cho phương tiện thay vì sử dụng malloc?
Aviad Rozenhek

Aviad: DirectShow sử dụng phân bổ ảo để dự trữ bộ nhớ, do đó nó có thể vượt qua các cờ cần thiết để tối ưu hóa hiệu suất, những thứ như không thể phân trang hoặc để nó có thể dự trữ các trang vật lý có thể cải thiện hiệu suất nếu phần cứng của bạn có thể hỗ trợ nó.
RandomNickName42

8
@ RandomNickName42: điều này không đúng. Địa chỉ bắt đầu phân bổ luôn được căn chỉnh theo độ chi tiết (64KB), nhưng độ dài phân bổ được làm tròn thành kích thước trang (4KB). Trên thực tế, không gian địa chỉ được dành riêng theo phần 64KB, nhưng được cam kết ở phần 4KB. Việc căn chỉnh địa chỉ bắt đầu thường không thú vị lắm, vì vậy từ hầu hết các quan điểm, VirtualAlloc hoạt động ở các phần 4KB. Những chi tiết này chỉ trở nên quan trọng nếu bạn đang cố gắng thực hiện nhiều VirtualAllocs nhỏ (bạn sẽ hết dung lượng địa chỉ) hoặc thứ gì đó lạ mắt (như phân bổ liền kề nhưng riêng biệt).
arx

Tôi nghĩ rằng malloc / new / CRT được gọi là HeapAlloc một lần sau đó sử dụng các thuật toán riêng của nó để trả về các khối bộ nhớ từ khối bộ nhớ HeapAlloc?
paulm

29

VirtualAlloclà một phân bổ chuyên biệt của hệ thống bộ nhớ ảo OS (VM). Phân bổ trong hệ thống VM phải được thực hiện ở mức độ chi tiết phân bổ (mức độ chi tiết phân bổ) phụ thuộc vào kiến ​​trúc. Phân bổ trong hệ thống VM là một trong những hình thức cấp phát bộ nhớ cơ bản nhất. Phân bổ VM có thể có nhiều dạng, bộ nhớ không nhất thiết phải dành riêng hoặc được hỗ trợ vật lý trong RAM (mặc dù có thể như vậy). Phân bổ VM thường là một loại phân bổ có mục đích đặc biệt , vì phân bổ phải

  • rất lớn,
  • cần được chia sẻ,
  • phải được căn chỉnh trên một giá trị cụ thể (lý do hiệu suất) hoặc
  • người gọi không cần sử dụng tất cả bộ nhớ này cùng một lúc ...
  • Vân vân...

HeapAllocvề cơ bản là những gì mallocnewcả hai cuối cùng gọi. Nó được thiết kế để rất nhanh và có thể sử dụng được trong nhiều loại kịch bản khác nhau của phân bổ mục đích chung. Nó là "Heap" theo nghĩa cổ điển. Các đống thực sự được thiết lập bởi a VirtualAlloc, là thứ được sử dụng để dự trữ không gian phân bổ ban đầu từ HĐH. Sau khi không gian được khởi tạo bởi VirtualAlloc, nhiều bảng, danh sách và cấu trúc dữ liệu khác được định cấu hình để duy trì và kiểm soát hoạt động của HEAP. Một số hoạt động đó ở dạng định cỡ động (phát triển và thu nhỏ) heap, điều chỉnh heap theo cách sử dụng cụ thể (phân bổ thường xuyên ở một số kích thước), v.v.

newmallochơi giống nhau, mallocvề cơ bản là một cuộc gọi chính xác vào HeapAlloc( heap-id-default ); newtuy nhiên, có thể [bổ sung] cấu hình bộ nhớ được cấp phát cho các đối tượng C ++ . Đối với một đối tượng nhất định, C ++ sẽ lưu trữ các vtables trên heap cho mỗi người gọi. Các vtables này là các chuyển hướng để thực thi và tạo thành một phần của những gì mang lại cho C ++ đó là các đặc tính OO như kế thừa, nạp chồng hàm, v.v.

Một số phương pháp phân bổ phổ biến khác như _alloca()_malloca()ngăn xếp dựa; FileMappings thực sự được cấp phát VirtualAllocvà thiết lập với các cờ bit cụ thể chỉ định các ánh xạ đó là loại FILE.

Hầu hết thời gian, bạn nên phân bổ bộ nhớ theo cách phù hợp với việc sử dụng bộ nhớ đó;). newtrong C ++, malloccho C, VirtualAlloccho các trường hợp lớn hoặc IPC.

*** Lưu ý, phân bổ bộ nhớ lớn được thực hiện bởi HeapAllocthực sự được chuyển đến VirtualAllocsau một số kích thước (vài trăm k hoặc 16 MB hoặc thứ gì đó tôi quên, nhưng khá lớn :)).

*** CHỈNH SỬA Tôi đã nhận xét ngắn gọn về IPC và VirtualAlloc, cũng có một cái gì đó rất gọn gàng về một liên quan VirtualAllocmà không ai trong số những người trả lời câu hỏi này thảo luận.

VirtualAllocEx là những gì một quy trình có thể sử dụng để cấp phát bộ nhớ trong không gian địa chỉ của một quy trình khác . Thông thường nhất, điều này được sử dụng kết hợp để thực thi từ xa trong ngữ cảnh của một quy trình khác thông qua CreateRemoteThread (tương tự như CreateThread, luồng chỉ được chạy trong quy trình khác).


29

Điều rất quan trọng là phải hiểu sự phân biệt giữa các API cấp phát bộ nhớ (trong Windows) nếu bạn định sử dụng ngôn ngữ yêu cầu quản lý bộ nhớ (như C hoặc C ++.) Và cách tốt nhất để minh họa IMHO là bằng sơ đồ:

nhập mô tả hình ảnh ở đây

Lưu ý rằng đây là chế độ xem rất đơn giản, dành riêng cho Windows.

Cách hiểu sơ đồ này là phương pháp cấp phát bộ nhớ trên sơ đồ càng cao thì phương pháp cấp phát bộ nhớ càng cao . Nhưng hãy bắt đầu từ phía dưới.

Trình quản lý bộ nhớ chế độ hạt nhân

Nó cung cấp tất cả các đặt bộ nhớ & phân bổ cho hệ điều hành, cũng như hỗ trợ cho các tập tin bộ nhớ ánh xạ , bộ nhớ chia sẻ , sao chép-on-write hoạt động, vv Đó là không thể truy cập trực tiếp từ mã sử dụng chế độ, vì vậy tôi sẽ bỏ qua nó ở đây.

VirtualAlloc / VirtualFree

Đây là các API cấp thấp nhất có sẵn từ chế độ người dùng . Các VirtualAllocchức năng cơ bản gọi ZwAllocateVirtualMemory mà lần lượt thực hiện một cách nhanh chóng syscall đến ring0để đổi đi xa tiếp tục xử lý để quản lý bộ nhớ hạt nhân. Đây cũng là phương pháp nhanh nhất để dự trữ / cấp phát khối bộ nhớ mới từ tất cả các bộ nhớ có sẵn trong chế độ người dùng.

Nhưng nó đi kèm với hai điều kiện chính:

  • Nó chỉ cấp phát các khối bộ nhớ được căn chỉnh trên ranh giới mức độ chi tiết của hệ thống.

  • Nó chỉ cấp phát các khối bộ nhớ có kích thước là bội số của mức độ chi tiết của hệ thống.

Vậy độ chi tiết của hệ thống này là gì? Bạn có thể lấy nó bằng cách gọi GetSystemInfo . Nó được trả về dưới dạng dwAllocationGranularitytham số. Giá trị của nó là việc triển khai (và có thể là phần cứng) cụ thể, nhưng trên nhiều hệ thống Windows 64-bit, nó được đặt theo 0x10000byte hoặc 64K.

Vì vậy, tất cả những điều này có nghĩa là nếu bạn cố gắng cấp phát, chỉ nói một khối bộ nhớ 8 byte với VirtualAlloc:

void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

Nếu thành công, pAddresssẽ được căn chỉnh trên 0x10000ranh giới byte. Và mặc dù bạn chỉ yêu cầu 8 byte, khối bộ nhớ thực tế mà bạn sẽ nhận được sẽ là toàn bộ page(hoặc một cái gì đó giống như 4Kbyte. Kích thước trang chính xác được trả về trong dwPageSizetham số.) Nhưng trên hết, toàn bộ khối bộ nhớ mở rộng 0x10000byte (hoặc 64Ktrong hầu hết các trường hợp) từ pAddress sẽ không có sẵn cho bất kỳ phân bổ nào nữa. Vì vậy, theo một nghĩa nào đó, bằng cách phân bổ 8 byte, bạn cũng có thể yêu cầu 65536.

Vì vậy, đạo lý của câu chuyện ở đây không phải là thay thế VirtualAlloccho việc phân bổ bộ nhớ chung trong ứng dụng của bạn. Nó phải được sử dụng cho các trường hợp rất cụ thể, như được thực hiện với đống bên dưới. (Thường để lưu trữ / cấp phát các khối bộ nhớ lớn.)

Sử dụng VirtualAllockhông đúng cách có thể dẫn đến phân mảnh bộ nhớ nghiêm trọng.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

Tóm lại, các hàm heap về cơ bản là một trình bao bọc cho VirtualAllochàm. Các câu trả lời khác ở đây cung cấp một khái niệm khá tốt về nó. Tôi sẽ nói thêm rằng, theo một cách nhìn rất đơn giản, cách heap hoạt động là:

  • HeapCreatedự trữ một khối lớn bộ nhớ ảo bằng cách gọi VirtualAllocnội bộ (hoặc ZwAllocateVirtualMemorycụ thể). Nó cũng thiết lập một cấu trúc dữ liệu bên trong có thể theo dõi các phân bổ kích thước nhỏ hơn nữa trong khối bộ nhớ ảo dành riêng.

  • Bất kỳ lệnh gọi nào đến HeapAllocHeapFreekhông thực sự cấp phát / giải phóng bất kỳ bộ nhớ mới nào (tất nhiên trừ khi, yêu cầu vượt quá những gì đã được dự trữ trong HeapCreate) nhưng thay vào đó chúng đo lường (hoặc commit) một đoạn lớn được dự trữ trước đó, bằng cách phân tích nó thành các khối bộ nhớ nhỏ hơn. người dùng yêu cầu.

  • HeapDestroyđến lượt các cuộc gọi VirtualFreethực sự giải phóng bộ nhớ ảo.

Vì vậy, tất cả những điều này làm cho các hàm heap trở thành ứng cử viên hoàn hảo cho việc phân bổ bộ nhớ chung trong ứng dụng của bạn. Nó rất tốt cho việc phân bổ bộ nhớ có kích thước tùy ý. Nhưng một cái giá nhỏ phải trả cho sự tiện lợi của các chức năng heap là chúng tạo ra một chi phí nhỏ VirtualAllockhi lưu trữ các khối bộ nhớ lớn hơn.

Một điều tốt khác về heap là bạn không thực sự cần tạo một. Nó thường được tạo cho bạn khi quá trình của bạn bắt đầu. Vì vậy, người ta có thể truy cập nó bằng cách gọi hàm GetProcessHeap .

malloc / miễn phí

Là một trình bao bọc ngôn ngữ cụ thể cho các hàm heap . Không giống như HeapAlloc, HeapFreev.v., các chức năng này sẽ hoạt động không chỉ nếu mã của bạn được biên dịch cho Windows mà còn cho các hệ điều hành khác (chẳng hạn như Linux, v.v.)

Đây là cách được khuyến nghị để cấp phát / giải phóng bộ nhớ nếu bạn lập trình trong C. (Trừ khi, bạn đang mã hóa một trình điều khiển thiết bị chế độ hạt nhân cụ thể.)

mới / xóa

Hãy trở thành một nhà khai thác quản lý bộ nhớ cấp cao (tốt, cho C++). Chúng dành riêng cho C++ngôn ngữ và giống như mallocđối với C, cũng là lớp bao bọc cho các heaphàm. Họ cũng có một loạt mã riêng của họ xử lý- C++khởi tạo cụ thể của các hàm tạo, phân bổ thỏa thuận trong các trình hủy, đưa ra một ngoại lệ, v.v.

Các hàm này là cách được khuyến nghị để cấp phát / giải phóng bộ nhớ và các đối tượng nếu bạn lập trình C++.


Cuối cùng, một nhận xét tôi muốn đưa ra về những gì đã được nói trong các phản hồi khác về việc sử dụng VirtualAllocđể chia sẻ bộ nhớ giữa các quy trình. VirtualAlloctự nó không cho phép chia sẻ bộ nhớ dành riêng / được cấp phát của nó với các tiến trình khác. Đối với điều đó, người ta cần sử dụng CreateFileMappingAPI có thể tạo một khối bộ nhớ ảo được đặt tên có thể được chia sẻ với các quy trình khác. Nó cũng có thể ánh xạ một tệp trên đĩa vào bộ nhớ ảo để truy cập đọc / ghi. Nhưng đó là một chủ đề khác.


7

Trong dàn ý:

  • VirtualAlloc, HeapAlloc, v.v. là các API Windows cấp phát trực tiếp bộ nhớ của nhiều loại khác nhau từ Hệ điều hành. VirtualAlloc quản lý các trang trong hệ thống bộ nhớ ảo Windows, trong khi HeapAlloc phân bổ từ một heap hệ điều hành cụ thể. Thành thật mà nói, bạn không bao giờ cần phải sử dụng cả hai.

  • malloc là một hàm thư viện C chuẩn (và C ++) cấp phát bộ nhớ cho quá trình của bạn. Việc triển khai malloc thường sẽ sử dụng một trong các API hệ điều hành để tạo một nhóm bộ nhớ khi ứng dụng của bạn khởi động và sau đó phân bổ từ nó khi bạn thực hiện các yêu cầu malloc

  • new là một toán tử C ++ tiêu chuẩn cấp phát bộ nhớ và sau đó gọi các hàm tạo một cách thích hợp trên bộ nhớ đó. Nó có thể được triển khai theo malloc hoặc theo các API hệ điều hành, trong trường hợp đó, nó thường sẽ tạo ra một vùng bộ nhớ khi khởi động ứng dụng.



2

VirtualAlloc=> Phân bổ thẳng vào bộ nhớ ảo, bạn dự trữ / cam kết theo khối. Điều này rất tốt cho các phân bổ lớn, ví dụ như mảng lớn.

HeapAlloc / new => phân bổ bộ nhớ trên heap mặc định (hoặc bất kỳ heap nào khác mà bạn có thể tạo). Điều này phân bổ cho mỗi đối tượng và rất tốt cho các đối tượng nhỏ hơn. Heap mặc định có thể tuần tự hóa do đó nó có phân bổ luồng đảm bảo (điều này có thể gây ra một số vấn đề trong các tình huống hiệu suất cao và đó là lý do tại sao bạn có thể tạo heap của riêng mình).

malloc=> sử dụng đống thời gian chạy C, tương tự như HeapAllocnhưng nó phổ biến cho các trường hợp tương thích.

Tóm lại, heap chỉ là một phần của bộ nhớ ảo được quản lý bởi trình quản lý heap (thay vì bộ nhớ ảo thô)

Mô hình cuối cùng trên thế giới bộ nhớ là các tệp được ánh xạ bộ nhớ, kịch bản này là rất tốt cho một lượng lớn dữ liệu (như tệp lớn). Điều này được sử dụng nội bộ khi bạn mở EXE (nó không tải EXE vào bộ nhớ, chỉ tạo một tệp ánh xạ bộ nhớ).

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.