Pointer (2) – Alokasi Dinamik

March 13, 2006 at 6:00 am | Posted in Algorithms, Tutorials | 1 Comment

by: Kusnassriyanto

Pada bagian ini kita akan membahas tiga hal yang masih saling berkaitan, yaitu alokasi memory secara dinamik, pointer liar, serta memory leak.

Mengalokasikan Memory secara Dinamik

Kita dapat meminta sebuah kapling tertentu di memori (heap memory). Bila masih tersedia, maka sistem operasi mencatatnya sebagai lokasi yang sedang digunakan dan memberikan alamat kapling yang kita minta tersebut. Pada saat kita meminta alokasi di memori, sistem operasi perlu informasi berapa ukuran memori yang kita perlukan. Delphi menyediakan empat cara untuk melakukan alokasi memory, yaitu menggunakan perintah New, GetMem, AllocMem, serta ReallocMem, dengan sintaks sebagai berikut

procedure New(var P: Pointer);
procedure GetMem(var P: Pointer; Size: Integer);
function AllocMem(Size: Cardinal): Pointer;
procedure ReallocMem(var P: Pointer; Size: Integer);

Seperti terlihat pada kode diatas, pada procedure GetMem, AllocMem, serta ReallocMem, kita harus mensuplai ukuran memori yang akan kita gunakan, sedangkan pada procedure New tidak. Pertanyaannya adalah bagaimana procedure New tahu seberapa besar ukuran memori yang harus dialokasikan. Jawabannya adalah berdasarkan tipe datanya. Pointer yang dilewatkan pada procedur New harus pointer bertipe sehingga procedure New dapat menentukan ukuran memory yang harus dialokasikan berdasarkan ukuran untuk tipe yang ditunjuk pointer tersebut. Bila kita melewatkan untyped pointer, maka memory yang dialokasikan adalah 0, dan in sangat berbahaya.

Cara lain untuk mengalokasikan memory adalah dengan menggunakan procedure GetMem dan function AllocMem. Keduanya membutuhkan informasi berapa ukuran memori yang harus dialokasikan. Dengan menggunakan kedua subrutin ini, kita dapat mengalokasikan memory yang ukurannya bebas. Pada function AllocMem, setiap byte memori yang telah dialokasikan akan diberi nilai awal 0, sedangkan pada GetMem tidak. Masih terdapat sebuah procedure yang dapat digunakan untuk mengalokasi memory, yaitu procedure ReallocMem yang dapat digunakan untuk melakukan realokasi (perubahan ukuran) terhadap data yang ditunjuk pointer tertentu. Data yang dapat direalokasi adalah data yang sebelumnya dialokasikan dengan GetMem, AllocMem, atau ReallocMem itu sendiri. Dengan asumsi bahwa ReallocMem melewatkan P dan Size, karakteristik ReAllocMem adalah sebagai berikut:

  • Jika P bernilai nil dan Size bernilai 0, maka ReallocMem tidak melakukan apapun
  • Jika P bernilai nil dan Size bernilai selain 0, maka ReallocMem akan mengalokasikan sebuah blok memori baru dan mencatat alamatnya dalam pointer P. Hal ini sama dengan yang dilakukan oleh GetMem
  • Jika P bernila selain nil dan Size bernilai 0, maka ReallocMem akan membebaskan memory yang ditunjuk oleh P dan menset nilai P dengan nil. Hal ini sama dengan yang dilakukan oleh FreeMem, kecuali bahwa FreeMem tidak mengisi P dengan nil.
  • Jika P bernilai selain nil dan Size bernilai selain 0, maka ReallocMem akan melakukan realokasi blok memori sehingga ukurannya sesuai dengan yang diminta. Selain itu, data lama tidak akan hilang. Bila ukuran baru lebih besar dari sebelumnya maka ukuran lebihnya tidak terdefinisi (karena tidak diinisialisasi).

Setelah memory tidak kita pergunakan lagi, kita harus membebaskan memori tersebut dengan salah satu dari procedure Dispose dan procedure FreeMem. Memori yang dialokasikan dengan procedure New harus dibebaskan dengan procedure Dispose, sedangkan memori yang dialokasikan dengan procedure GetMem maupun AllocMem harus dibebaskan dengan procedure FreeMem.

type
  PTMyArray = ^TMyArray;
  TMyArray = array[0..100] of Integer;
var
  A, B, C: PTMyArray;
  i: Integer;
begin
  {Alokasikan memory, simpan alamatnya di A}
  New(A);
  for i:=Low(A^) to High(A^) do begin
    A^[i] := 0;
  end;
  {C menunjuk alamat yang sama dengan A}
  C := A;
  {Alokasikan memory, simpan alamatnya di B}
  New(B);
  for i:=Low(B^) to High(B^) do begin
    B^[i] := 1;
  end;
  {copy isi alamat yang ditunjuk B ke alamat
    yang ditunjuk C}
  C^ := B^;
  Dispose(A);
  Dispose(B);
end;

Ilustrasi

Pointer Liar

Dalam penggunaan pointer, terdapat dua macam salah pakai terhadap pointer yang sering terjadi, yaitu pointer liar dan memory leak. Pointer liar adalah pointer yang menunjuk ke suatu alamat yang tidak valid. Contohnya ketika kita baru mendefinisikan pointer p1 dan belum melakukan inisialisasi, maka pointer tersebut menunjuk ke suatu tempat yang tidak terdefinisi. Bila kita mengakses p1^, maka akan terjadi error. Contoh lainnya adalah ketika kita mengalokasikan kapling di memori untuk p2, kemudian kita sudah membebaskan memori tersebut, tetapi kita masih mengaksesnya, maka akan terjadi error. Variabel pointer p1 dan p2 tersebut adalah pointer liar. Untuk lebih jelasnya, perhatikan kode berikut ini.

var
  p1: ^Integer;
  p2: ^Integer;
begin
  New(p1);
  p2 := p1;
  Dispose(p2);
  p1^ := 100;
end;

Ilustrasi

Dari ilustrasi diatas terlihat bahwa bila setelah langkah ketiga kita mengisi alamat yang ditunjuk oleh p1 dengan seratus (p1^ := 100;), maka akan terjadi error karena setelah langkah ketiga p1 dan p2 menunjuk ke tempat yang sudah tidak terdefinisi lagi karena sudah di-dispose.

Memory Leak

Memory leak adalah suatu blok di heap memory yang oleh sistem operasi masih ditandai sebagai sedang digunakan, tetapi sebenarnya sudah ditinggalkan oleh aplikasi. Untuk lebih jelasnya, perhatikan contoh kode berikut ini.

procedure MemLeak;
var
  p1: ^Integer;
begin
  New(p1);      // Alokasi memori
  p1^ := 100;   // p1^ diisi nilai tertentu
  Writeln(p1^); // p1^ digunakan
end; // keluar dari procedure tanpa membebaskan memori 

Ilustrasi

Setelah keluar dari procedure, p1 sudah tidak valid lagi. Tetapi memori yang sebelumnya ditunjuk oleh p1 masih tetap ditandai unavailable. Pada keadaan ini, aplikasi telah kehilangan link sehingga tidak mungkin membebaskan memori tersebut. Akibatnya terjadi lubang di memori. Bila procedure MemLeak diatas dipanggil berulang-ulang, maka akan semakin banyak memory leak. Berbeda dengan wild pointer yang langsung mengakibatkan error, memory leak tidak langsung menimbulkan masalah pada saat pengembangan program. Meskipun demikian, memory leak harus diwaspadai karena bisa menjadi bom waktu yang meledak pada saat kritis.

Bila anda sering menggunakan perintah exit di tengah procedure, maka waspadalah ketika anda bekerja dengan pointer, program anda rawan kesalahan yang menyebabkan memory leak. Perhatikan contoh berikut ini.

procedure MemLeak2;
var
  p1: ^Integer;
begin
  New(p1);
  p1^ := 100;
  if (kondisi_tertentu) then begin
    exit;
  end;
  Writeln(p1^);
  Dispose(p1);
end;

Jika kondisi_tertentu bernilai true, maka procedure Dispose tidak dipangil, sehingga menimbulkan memory leak. Untuk menghindarinya, biasakanlah untuk tidak menggunakan exit. Bila kondisi memaksa anda untuk menggunakan exit, sebaiknya sebelum perintah exit dipanggil, memori dibebaskan terlebih dahulu.

Kebiasaan lain yang lebih sering dilakukan adalah tidak melindungi alokasi resource dari exception, seperti diilustrasikan pada contoh berikut ini.

procedure MemLeak3;
var
  p1: ^Integer;
begin
  New(p1);
  p1^ := 100;
  MyProcedure(p1);
  Writeln(p1);
  Dispose(p1);
end;

Bila pada saat memanggil MyProcedure terjadi exception, maka control program akan langsung menuju akhir dari procedure. Akibatnya procedure Dispose tidak dipanggil yang menyebabkan munculnya memory leak. Untuk mengatasinya, biasakanlah menggunakan sekenario try … finally seperti ditunjukkan pada contoh berikut ini.

procedure MemLeak4;
var
  p1: ^Integer;
begin
  New(p1);
  try
    p1^ := 100;
    MyProcedure(p1);
    Writeln(p1);
  finally
    Dispose(p1);
  end;
end;

Pada contoh diatas, bila terjadi error pada MyProcedure(p1), maka kontrol program akan bergerak menuju finally, sehingga procedure Dispose tetap dijalankan. Bila tidak terjadi exception pun procedure Dispose tetap dijalankan.

Catatan :
Perhatikan bahwa procecure New dipanggil sebelum try. Sejumlah programmer sering melakukan kesalahan dalam meletakkan procedure alokasi memory di dalam blok try.

Cara yang dapat digunakan untuk mendeteksi terjadinya memory leak adalah menggunakan tools yang khusus diperuntukkan untuk hal seperti ini, misalnya MemCheck dari Vincent Mahon.

(bersambung …)

1 Comment »

RSS feed for comments on this post. TrackBack URI

  1. Kalau penjumlahan bilangan biner?

    Source codenya sperti apa?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.
Entries and comments feeds.

%d bloggers like this: