Go Memory Layout

1. Apa Itu Memory Layout?

Setiap variabel di Go (dan bahasa pemrograman lain) disimpan di RAM. Bayangkan RAM seperti rak besar berisi kotak-kotak kecil (byte) yang berjejer.

Contoh:

Alamat memori (hex): 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | ...
Isi tiap kotak (byte):  ??   ??    ??    ??    ??   ...

Nah, ketika kamu punya struct seperti ini:

type SimpleStruct struct {
    a int8
    b int16
    c int32
    d int64
}

Go harus menyimpan semua field ini berurutan di memori. Tapi, CPU punya aturan alignment yang membuat tidak semua data bisa langsung ditempel rapat satu sama lain.


2. Alignment dan Padding: Kenapa Ada "Ruang Kosong"?

Alignment

Setiap tipe data di Go (dan bahasa lain seperti C) punya alignment requirement β€” yaitu bahwa data harus dimulai di alamat memori yang merupakan kelipatan tertentu.

Tipe Data
Ukuran
Alignment

int8

1 byte

1 byte

int16

2 byte

2 byte

int32

4 byte

4 byte

int64

8 byte

8 byte

Artinya:

  • int8 boleh mulai di mana saja.

  • int16 harus mulai di alamat yang kelipatan 2 (misalnya 0, 2, 4, 6, dst).

  • int32 harus mulai di alamat yang kelipatan 4.

  • int64 harus mulai di alamat yang kelipatan 8.

Kalau posisi field berikutnya tidak sesuai alignment-nya, Go akan menyisipkan padding byte kosong supaya tetap sejajar dengan aturan CPU.


3. Visualisasi Step-by-Step Memory Layout

Kita lihat layout struct berikut di sistem 64-bit:


Field a (int8)

  • Ukuran: 1 byte

  • Alignment: 1 byte

  • Diletakkan mulai di alamat 0.


Field b (int16)

  • Ukuran: 2 byte

  • Alignment: 2 byte

  • Field sebelumnya (a) berakhir di byte ke-1 β†’ byte berikutnya adalah 1, bukan kelipatan 2 β†’ harus ada padding 1 byte.


Field c (int32)

  • Ukuran: 4 byte

  • Alignment: 4 byte

  • Field b berakhir di byte 3 β†’ byte berikutnya adalah 4, sudah kelipatan 4, jadi tidak perlu padding.


Field d (int64)

  • Ukuran: 8 byte

  • Alignment: 8 byte

  • Field c berakhir di byte 7, jadi byte berikutnya 8, sudah kelipatan 8, tidak perlu padding di sini.


Kenapa Masih Ada Padding di Akhir?

Struct di Go juga disesuaikan agar ukuran total struct adalah kelipatan dari alignment terbesar di dalam struct (di sini: 8 byte). Tujuannya agar ketika array of struct dibuat, setiap struct tetap sejajar secara optimal di memori.

Sekarang total byte kita baru sampai byte 15. Supaya kelipatan 8, Go menambahkan 8 byte padding di akhir β†’ jadi total 24 byte.


4. Total Size Perhitungan

Bagian
Byte

a (int8)

1

padding (setelah a)

1

b (int16)

2

c (int32)

4

d (int64)

8

padding (akhir struct)

8

Total

24 byte


5. Kenapa Bukan 15 Byte?

Kalau dihitung mentah:

Tapi komputer tidak boleh menyimpan sembarangan di memori tanpa memperhatikan alignment. Kalau kamu β€œmaksa”, CPU harus melakukan operasi baca/tulis byte demi byte (bukan per word) β€” ini membuatnya lebih lambat bahkan bisa crash di beberapa arsitektur.

Jadi, Go menambahkan padding otomatis supaya semuanya sejajar, efisien, dan aman bagi CPU.


6. Visualisasi Akhir (64-bit)


7. Tips: Cara Mengecek Sendiri di Go

Kamu bisa lihat ukuran dan offset tiap field pakai unsafe:

Outputnya akan kira-kira:


8. Bonus: Mengurangi Ukuran Struct

Kalau kamu urutkan field dari yang besar ke kecil, padding bisa berkurang:

Ini bisa membuat total hanya 16 byte, bukan 24, karena padding di tengah bisa dihindari.


Ringkasan

Konsep
Penjelasan

Memory layout

Urutan field struct di memori

Alignment

Data harus dimulai di alamat kelipatan ukuran tertentu

Padding

Byte kosong untuk menjaga alignment

Ukuran struct

Dibulatkan ke kelipatan alignment terbesar

Optimasi

Urutkan field dari besar ke kecil agar lebih rapat


Tutorial Lengkap Memory Layout di Go

Dari Dasar Hingga Advanced dengan Praktik Kode


Daftar Isi


1. Pengenalan Memory Layout

1.1 Apa itu Memory Layout?

Memory layout adalah cara kompiler Go mengatur dan menempatkan data dalam memori. Pemahaman tentang memory layout sangat penting untuk:

  • Optimisasi penggunaan memori - Menghemat RAM dengan mengurangi padding

  • Peningkatan performa - CPU lebih efisien mengakses data yang aligned dengan benar

  • Keamanan concurrent programming - Penggunaan atomic operations yang tepat

  • Debugging - Memahami ukuran sebenarnya dari struct

1.2 Konsep Fundamental

Alignment (Penjajaran)

Alamat memori tempat data dimulai harus merupakan kelipatan dari nilai tertentu (N). Nilai N ini disebut alignment guarantee.

Padding (Isian)

Byte-byte tambahan yang disisipkan oleh kompiler untuk memenuhi aturan alignment.

Size (Ukuran)

Total memori yang digunakan oleh sebuah tipe data, termasuk padding.

1.3 Kode Dasar: Memeriksa Size dan Alignment

Output (pada arsitektur 64-bit):


2. Type Alignment Guarantees

2.1 Aturan Alignment di Go

Go memiliki aturan alignment yang berbeda untuk setiap tipe data:

Tipe Data
Alignment (bytes)

bool, int8, uint8

1

int16, uint16

2

int32, uint32, float32

4

int64, uint64, float64, complex64

8 (64-bit) / 4 (32-bit)

complex128

8

pointer, string, slice, map, channel

8 (64-bit) / 4 (32-bit)

array

Sama dengan elemen array

struct

Alignment terbesar dari field-fieldnya

2.2 General vs Field Alignment

Ada dua jenis alignment guarantee:

  1. General Alignment: Digunakan untuk variable declaration, array elements, dll.

  2. Field Alignment: Digunakan ketika tipe tersebut menjadi field dari struct.

Pada compiler standar Go (gc), kedua nilai ini selalu sama. Namun pada gccgo, bisa berbeda.

2.3 Kode: Memeriksa Alignment Detail

Output:

2.4 Visualisasi Memory Layout


3. Type Sizes dan Structure Padding

3.1 Mengapa Structure Padding Penting?

Padding memastikan setiap field memulai pada alamat yang sesuai dengan alignment requirement-nya. Urutan field sangat mempengaruhi ukuran total struct!

3.2 Contoh: Dampak Urutan Field

Output:

3.3 Visualisasi Memory Layout

3.4 Kode: Analyzer Tool untuk Struct

3.5 Zero-Sized Fields

Field dengan ukuran 0 byte (seperti struct{} atau [0]int) kadang dapat mempengaruhi padding:

Output:


4. Alignment untuk 64-bit Atomic Operations

4.1 Masalah pada Arsitektur 32-bit

Pada arsitektur 32-bit, alignment guarantee untuk int64 dan uint64 hanya 4 bytes, tetapi operasi atomic 64-bit memerlukan alignment 8 bytes. Jika tidak terpenuhi, program akan panic di runtime!

4.2 Aturan Keamanan

Menurut dokumentasi sync/atomic:

On 32-bit systems (ARM, x86-32), the 64-bit functions use instructions unavailable before certain CPU generations.

It is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

4.3 Kata yang Aman untuk Atomic Operations

Pada arsitektur 32-bit, 64-bit word berikut dijamin 8-byte aligned:

  1. Variable global

  2. Variable lokal yang dideklarasikan dengan var

  3. Elemen pertama dari allocated array/slice

  4. Field pertama dari allocated struct

  5. Value yang dikembalikan oleh new() atau make()

4.4 Contoh: Safe vs Unsafe Atomic Access

4.5 Solusi untuk Arsitektur 32-bit

Solusi 1: Gunakan Field Pertama

Solusi 2: Gunakan Array Alignment Trick

Solusi 3: Gunakan atomic.Int64/Uint64 (Go 1.19+)

4.6 Testing Atomic Safety


5. Optimisasi Memory Layout

5.1 Prinsip Optimisasi

Untuk mengoptimalkan memory layout:

  1. Urutkan field dari besar ke kecil (descending by size)

  2. Kelompokkan field dengan ukuran sama

  3. Letakkan field yang sering diakses bersamaan berdekatan (cache locality)

  4. Pertimbangkan false sharing untuk concurrent access

5.2 Contoh Optimisasi Lengkap

Output:

5.3 Cache Line Optimization (False Sharing)

False sharing terjadi ketika dua goroutine mengakses field berbeda dalam struct yang sama, tapi field tersebut berada dalam cache line yang sama.

5.4 Tool: Automatic Layout Optimizer


6. Advanced Topics

6.1 Embedded Structs

6.2 Interface Values

6.3 Slice dan String Internals

6.4 Map Internals (Simplified)

6.5 Channel Internals


7. Best Practices

7.1 Checklist untuk Memory Layout

βœ… DO:

  1. Urutkan field dari alignment terbesar ke terkecil

  2. Kelompokkan field yang sering diakses bersama

  3. Gunakan atomic types untuk concurrent counters

  4. Pertimbangkan cache line size untuk concurrent access

❌ DON'T:

  1. Jangan letakkan field kecil di antara field besar

  2. Jangan gunakan 64-bit atomic pada field yang bukan pertama (32-bit)

  3. Jangan abaikan padding saat menghitung ukuran struct

7.2 Performance Tips

Tip 1: Gunakan Profiling Tools

Tip 2: Benchmark Different Layouts

7.3 Common Patterns

Pattern 1: Hot/Cold Fields

Pattern 2: Embedding untuk Reusability

Pattern 3: Memory Pool dengan Optimal Layout

7.4 Debugging Tools

Tool 1: Struct Layout Visualizer

Tool 2: Memory Layout Comparator

7.5 Production Checklist

Sebelum deploy ke production, periksa:

  • [ ] Struct besar sudah dioptimasi layout-nya

  • [ ] Field yang sering diakses concurrent sudah menggunakan atomic types

  • [ ] Tidak ada 64-bit atomic operation pada field non-pertama (untuk support 32-bit)

  • [ ] Cache line padding ditambahkan untuk high-contention concurrent access

  • [ ] Profiling sudah dilakukan untuk memastikan tidak ada memory bloat

  • [ ] Documentation ditambahkan untuk layout yang critical


8. Real-World Examples

8.1 HTTP Server Stats

8.2 Cache Entry

8.3 Ring Buffer

8.4 Metrics Collector


9. Summary & Quick Reference

9.1 Alignment Rules Quick Reference

Type
Size
Alignment (64-bit)
Alignment (32-bit)

bool, int8, uint8

1

1

1

int16, uint16

2

2

2

int32, uint32, float32

4

4

4

int64, uint64, float64

8

8

4

complex64

8

8

4

complex128

16

8

4

pointer, uintptr

8/4

8

4

string

16/8

8

4

slice

24/12

8

4

map, channel, func

8/4

8

4

9.2 Optimization Workflow

9.3 Key Takeaways

  1. Struct size β‰  sum of field sizes - Padding adds overhead

  2. Field order matters - Can save 30-50% memory

  3. 64-bit atomic requires 8-byte alignment - Critical on 32-bit

  4. Cache lines are 64 bytes - Prevent false sharing

  5. Use atomic types - Safer than manual atomic operations

  6. Profile first - Don't optimize prematurely

  7. Document layout decisions - Help future maintainers

9.4 Common Mistakes to Avoid

9.5 Useful Functions Reference


10. Further Resources

Official Documentation

Tools

  • go tool compile -S - View assembly to understand memory layout

  • go test -bench -benchmem - Measure memory allocations

  • pprof - Profile memory usage in production

Best Practices

  • Profile before optimizing

  • Document non-obvious layout decisions

  • Use code comments to explain padding

  • Test on both 32-bit and 64-bit if supporting both

  • Consider using go vet for atomic alignment issues (Go 1.22+)


Appendix: Complete Working Examples

Example A: Layout Analyzer Tool


Kesimpulan:

Memory layout adalah aspek fundamental dalam pemrograman Go yang sering diabaikan. Dengan memahami alignment, padding, dan optimisasi layout, Anda dapat:

  • Mengurangi penggunaan memori hingga 30-50%

  • Meningkatkan performa cache CPU

  • Menulis concurrent code yang aman dan efisien

  • Menghindari subtle bugs pada atomic operations

Ingat: Profile first, optimize second. Gunakan pengetahuan ini ketika profiling menunjukkan memory atau performance bottleneck.

Last updated