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:
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ớ.
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ổ.
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
. HeapAlloc
biết khi nào nó cần gọi VirtualAlloc
và 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.
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 malloc
cuộc gọiHeapAlloc
.
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 new
cuộ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_ptr
thế. 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 SysAllocString
mà 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ể.
VirtualAlloc
là 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
HeapAlloc
về cơ bản là những gì malloc
và new
cả 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.
new
và malloc
hơi giống nhau, malloc
về cơ bản là một cuộc gọi chính xác vào HeapAlloc( heap-id-default )
; new
tuy 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()
và _malloca()
là ngăn xếp dựa; FileMappings thực sự được cấp phát VirtualAlloc
và 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ớ đó;). new
trong C ++, malloc
cho C, VirtualAlloc
cho 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 HeapAlloc
thực sự được chuyển đến VirtualAlloc
sau 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 VirtualAlloc
mà không ai trong số những người trả lời câu hỏi này thảo luận.
VirtualAlloc
Ex 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).
Đ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ơ đồ:
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.
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.
Đây là các API cấp thấp nhất có sẵn từ chế độ người dùng . Các VirtualAlloc
chứ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 dwAllocationGranularity
tham 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 0x10000
byte 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, pAddress
sẽ được căn chỉnh trên 0x10000
ranh 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ư 4K
byte. Kích thước trang chính xác được trả về trong dwPageSize
tham số.) Nhưng trên hết, toàn bộ khối bộ nhớ mở rộng 0x10000
byte (hoặc 64K
trong 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ế VirtualAlloc
cho 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 VirtualAlloc
không đúng cách có thể dẫn đến phân mảnh bộ nhớ nghiêm trọng.
Tóm lại, các hàm heap về cơ bản là một trình bao bọc cho VirtualAlloc
hà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à:
HeapCreate
dự trữ một khối lớn bộ nhớ ảo bằng cách gọi VirtualAlloc
nội bộ (hoặc ZwAllocateVirtualMemory
cụ 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 HeapAlloc
và HeapFree
khô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 VirtualFree
thự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ỏ VirtualAlloc
khi 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 .
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
, HeapFree
v.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ể.)
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 heap
hà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. VirtualAlloc
tự 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 CreateFileMapping
API 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.
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.
VirtualAlloc
===> sbrk()
dưới UNIX
HeapAlloc
====> malloc()
theo UNIX
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ư HeapAlloc
như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ớ).