Bagaimana gratis tahu berapa banyak yang gratis?

385

Dalam pemrograman C, Anda dapat melewatkan pointer apa pun yang Anda suka sebagai argumen untuk dibebaskan, bagaimana ia mengetahui ukuran memori yang dialokasikan untuk dibebaskan? Setiap kali saya melewatkan pointer ke beberapa fungsi, saya juga harus melewati ukuran (yaitu array 10 elemen perlu menerima 10 sebagai parameter untuk mengetahui ukuran array), tetapi saya tidak harus meneruskan ukuran ke fungsi bebas. Mengapa tidak, dan bisakah saya menggunakan teknik yang sama ini dalam fungsi saya sendiri untuk menyelamatkan saya dari kebutuhan untuk menggerakkan variabel tambahan dari panjang array?

Joshua Cheek
sumber
Pertanyaan serupa: stackoverflow.com/questions/851958/… (meskipun saya akan mengatakan itu tidak cukup duplikat)
John Carter
Sistem buddy adalah cara lain untuk melakukannya yang dapat menentukan berdasarkan pointer, tanpa overhead di setiap blok.
EvilTeach
Posting ini menjelaskannya dengan baik: stackoverflow.com/questions/1957099/…
Zeeshan Mahmood

Jawaban:

349

Saat Anda menelepon malloc(), Anda menentukan jumlah memori yang akan dialokasikan. Jumlah memori yang sebenarnya digunakan sedikit lebih dari ini, dan termasuk informasi tambahan yang merekam (setidaknya) seberapa besar bloknya. Anda tidak dapat (andal) mengakses informasi lain itu - dan Anda juga tidak boleh :-).

Ketika Anda menelepon free(), itu hanya melihat informasi tambahan untuk mengetahui seberapa besar blok itu.

Gary McGill
sumber
44
FYI, misalnya BSD harus secara malloc_size()andal mengakses ukuran blok dari malloc()penunjuk ed. Tetapi tidak ada cara yang andal dan portabel.
laalto
50
Saya pikir penting untuk mengatakan bahwa blok informasi tambahan ini terletak sebelum pointer dikembalikan.
Georg Schölly
39
@gs Yah itu tergantung implementasi. Tapi, ya, di situlah biasanya.
Falaina
31
Bisakah Anda bayangkan kengeriannya jika free()mengharuskan programmer melaporkan secara akurat seberapa besar malloc()bloknya? Kebocoran memori sudah cukup buruk.
MusiGenesis
35
Mengapa informasi itu tersedia untuk malloc()dan free(), tetapi Anda harus menyimpan ukuran array? Mengapa mereka tidak memungkinkan untuk melakukan sesuatu seperti blockSize(ptr)jika mereka menyimpan informasi itu?
corsiKa
144

Sebagian besar implementasi fungsi alokasi memori C akan menyimpan informasi akuntansi untuk setiap blok, baik in-line atau secara terpisah.

Salah satu cara khas (in-line) adalah untuk benar-benar mengalokasikan header dan memori yang Anda minta, diisi hingga beberapa ukuran minimum. Jadi misalnya, jika Anda meminta 20 byte, sistem dapat mengalokasikan blok 48 byte:

  • Header 16 byte yang berisi ukuran, marker khusus, checksum, pointer ke blok berikutnya / sebelumnya dan seterusnya.
  • Area data 32 byte (20 byte Anda diisi hingga kelipatan 16).

Alamat yang kemudian diberikan kepada Anda adalah alamat area data. Kemudian, ketika Anda membebaskan blok, freehanya akan mengambil alamat yang Anda berikan dan, dengan asumsi Anda belum memasukkan alamat itu atau memori di sekitarnya, periksa informasi akuntansi segera sebelum itu. Secara grafis, itu akan sepanjang garis:

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

Perlu diingat ukuran header dan padding sepenuhnya implementasi didefinisikan (sebenarnya, semuanya didefinisikan implementasi (a) tetapi opsi akuntansi in-line adalah yang umum).

Checksum dan spidol khusus yang ada dalam informasi akuntansi sering menjadi penyebab kesalahan seperti "Memory Arena rusak" atau "Gratis ganda" jika Anda menimpa mereka atau membebaskan mereka dua kali.

Padding (untuk membuat alokasi lebih efisien) adalah mengapa Anda kadang-kadang dapat menulis sedikit di luar batas ruang yang Anda minta tanpa menimbulkan masalah (tetap saja, jangan lakukan itu, itu perilaku yang tidak ditentukan dan, hanya karena itu bekerja kadang-kadang, tidak t berarti tidak apa-apa untuk melakukannya).


(a) Saya sudah menulis implementasi mallocdalam sistem embedded di mana Anda mendapat 128 byte tidak peduli apa yang Anda minta (itu adalah ukuran struktur terbesar dalam sistem), dengan asumsi Anda meminta 128 byte atau kurang (permintaan untuk lebih banyak akan harus dipenuhi dengan nilai pengembalian NULL). Bit-mask yang sangat sederhana (yaitu, bukan in-line) digunakan untuk memutuskan apakah sepotong 128-byte dialokasikan atau tidak.

Yang lain yang saya kembangkan memiliki kumpulan yang berbeda untuk potongan 16-byte, potongan 64-byte, potongan 256-byte, dan potongan 1K, sekali lagi menggunakan bit-mask untuk memutuskan blok apa yang digunakan atau tersedia.

Kedua opsi ini berhasil mengurangi overhead informasi akuntansi dan untuk meningkatkan kecepatan mallocdan free(tidak perlu menyatukan blok yang berdekatan ketika membebaskan), terutama penting dalam lingkungan tempat kami bekerja.

paxdiablo
sumber
@ paxdiablo Apakah itu berarti malloc tidak mengalokasikan blok memori yang berdekatan?
user10678
2
@ user10678, satu-satunya persyaratan nyata mallocadalah bahwa ia memberi Anda, untuk kasus yang berhasil, satu blok memori setidaknya sebesar yang Anda minta. Masing-masing blok berdekatan dalam hal bagaimana Anda mengakses elemen di dalamnya, tetapi tidak ada persyaratan bahwa arena dari mana blok berasal berdekatan.
paxdiablo
Pertanyaan terkait: Mengapa tidak ada variasi malloc / gratis, di mana Anda menentukan ukuran ketika membebaskan dan sehingga tidak perlu menyimpan ukuran?
user253751
@ user253751, karena dengan demikian satu hal lagi yang perlu Anda perhatikan, di atas dan di atas pointer itu sendiri. Ini baik tidak perlu dan berbahaya: void *x = malloc(200); free(x, 500);yang tidak akan berakhir dengan baik :-) Dalam kasus apapun, untuk efisiensi, yang sebenarnya ukuran buffer mungkin lebih besar (Anda hanya tidak bisa mengandalkan ini).
paxdiablo
@ paxdiablo Ini juga menghindari pemborosan memori untuk menahan ukuran.
user253751
47

Dari comp.lang.cdaftar FAQ: Bagaimana gratis tahu berapa byte hingga gratis?

Implementasi malloc / free mengingat ukuran setiap blok saat dialokasikan, sehingga tidak perlu mengingatkannya tentang ukuran ketika membebaskan. (Biasanya, ukuran disimpan berdekatan dengan blok yang dialokasikan, itulah sebabnya hal-hal biasanya rusak parah jika batas-batas blok yang dialokasikan bahkan sedikit melampaui batas)

jdehaan
sumber
2
Ini bukan jawaban. Pertanyaannya persis seperti ini: mengapa bisa dengan bebas mencari ukuran blok, tetapi belum ada fungsi yang tersedia untuk programmer yang melakukan itu?
Bananach
Ini memang detail implementasi untuk api malloc dan tidak ada api untuk mendapatkan info ini kembali dengan cara standar (setahu saya). "Sistem" merekamnya dan menggunakannya pada free. Mungkin jawabannya tidak memuaskan Anda, tetapi saya tidak berpikir Anda akan mendapatkannya dengan info yang lebih umum berlaku :-)
jdehaan
6

Jawaban ini dipindahkan dari Bagaimana free () tahu berapa banyak memori yang harus dialokasikan? di mana saya dicegah untuk menjawab dengan pertanyaan duplikat yang jelas. Maka jawaban ini harus relevan dengan duplikat ini:


Untuk kasus malloc, pengalokasi tumpukan menyimpan pemetaan dari pointer dikembalikan asli, untuk rincian yang relevan diperlukan untuk freememori nanti. Ini biasanya melibatkan penyimpanan ukuran wilayah memori dalam bentuk apa pun yang relevan dengan pengalokasi yang digunakan, misalnya ukuran mentah, atau simpul dalam pohon biner yang digunakan untuk melacak alokasi, atau jumlah "unit" memori yang digunakan.

freetidak akan gagal jika Anda "mengganti nama" pointer, atau menggandakannya dengan cara apa pun. Namun referensi tidak dihitung, dan hanya yang pertama yang freeakan benar. Tambahan freeadalah kesalahan "bebas ganda".

Mencoba freesetiap penunjuk dengan nilai yang berbeda dengan yang dikembalikan oleh mallocs sebelumnya , dan yang belum diperbaiki adalah kesalahan. Tidak dimungkinkan untuk membebaskan sebagian wilayah memori yang kembali dari malloc.

Matt Joiner
sumber
Saya mengubah nilai pointer yang dikembalikan oleh panggilan malloc. Dan saya membebaskannya tanpa kesalahan. Mengapa? Lihat di sini: stackoverflow.com/questions/42618390/…
smwikipedia
4

Pada catatan terkait, pustaka GLib memiliki fungsi alokasi memori yang tidak menyimpan ukuran implisit - dan kemudian Anda hanya meneruskan parameter ukurannya menjadi bebas. Ini dapat menghilangkan bagian dari overhead.

EFRAIM
sumber
3

malloc()dan free()tergantung pada sistem / kompiler sehingga sulit untuk memberikan jawaban yang spesifik.

Informasi lebih lanjut tentang pertanyaan lain ini .

LiraNuna
sumber
2
Mereka benar-benar bergantung pada perpustakaan (biasanya perpustakaan C, yang biasanya terkait erat dengan OS). Untuk kompiler, mereka hanya berfungsi.
Donal Fellows
2

Manajer tumpukan menyimpan jumlah memori milik blok yang dialokasikan di suatu tempat ketika Anda menelepon malloc.

Saya tidak pernah menerapkannya sendiri, tetapi saya kira memori tepat di depan blok yang dialokasikan mungkin berisi informasi meta.

Timbo
sumber
3
Itu salah satu implementasi yang mungkin, tetapi orang bisa merancang sistem di mana semua memori dilacak dalam satu tabel di halaman yang sama sekali berbeda, tidak harus di mana saja dekat dengan kolam memori yang dialokasikan.
ephemient
2

Teknik asli adalah untuk mengalokasikan blok yang sedikit lebih besar dan menyimpan ukuran di awal, lalu berikan aplikasi sisa blog. Ruang ekstra menampung ukuran dan mungkin tautan untuk menyatukan blok gratis untuk digunakan kembali.

Namun ada beberapa masalah dengan trik-trik itu, seperti cache yang buruk dan perilaku manajemen memori. Menggunakan memori tepat di blok cenderung halaman hal-hal yang tidak perlu dan juga membuat halaman kotor yang mempersulit berbagi dan copy-on-write.

Jadi teknik yang lebih maju adalah menyimpan direktori terpisah. Pendekatan eksotis juga telah dikembangkan di mana area memori menggunakan kekuatan dua ukuran yang sama.

Secara umum, jawabannya adalah: struktur data terpisah dialokasikan untuk menjaga status.

DigitalRoss
sumber
1

Untuk menjawab bagian kedua dari pertanyaan Anda: ya, Anda bisa, dan pola yang cukup umum di C adalah sebagai berikut:

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;
MSalters
sumber
Itu teknik yang sama sekali berbeda dengan yang digunakan BSD malloc untuk benda-benda kecil (meskipun itu teknik yang sangat baik untuk membuat array gaya Pascal)
Pete Kirkham
0

Ketika kita memanggil malloc, itu hanya mengkonsumsi lebih banyak byte dari persyaratannya. Konsumsi byte lebih ini berisi informasi seperti jumlah cek, ukuran dan informasi tambahan lainnya. Ketika kami menelepon gratis pada saat itu langsung ke informasi tambahan di mana ia menemukan alamat dan juga menemukan berapa banyak blok akan bebas.

Varun Chhangani
sumber
0

untuk menjawab pertanyaan kedua, ya Anda bisa (jenis) menggunakan teknik yang sama seperti malloc() dengan hanya menugaskan sel pertama di dalam setiap array ke ukuran array. yang memungkinkan Anda mengirim array tanpa mengirim argumen ukuran tambahan.

avish
sumber