Golang / Golang Internals and Memory Management Interview Questions
How are Go channels implemented internally?
A channel is a typed, goroutine-safe FIFO queue managed by the runtime. Internally it is a hchan struct containing a circular ring buffer (for buffered channels), send and receive queues of waiting goroutines, a mutex, and metadata like element type, capacity, and current length.
// Unbuffered channel â synchronous rendezvous ch := make(chan int) // cap=0, buf=nil // Send blocks until a goroutine receives; receive blocks until send // Buffered channel â asynchronous up to capacity ch2 := make(chan int, 5) // cap=5, ring buffer of 5 ints ch2 <- 1 // does not block (buffer not full) ch2 <- 2 v := <-ch2 // 1 (FIFO) // Select â multiplexed channel operations select { case msg := <-ch: fmt.Println("received", msg) case ch2 <- 42: fmt.Println("sent 42") case <-time.After(1 * time.Second): fmt.Println("timeout") default: fmt.Println("non-blocking â no case ready") } // Closing a channel â signals receivers: no more data close(ch2) val, ok := <-ch2 // ok=false means channel closed and empty // Range over channel â reads until closed for v := range ch2 { fmt.Println(v) } // Common rules: // - Sending to a closed channel panics // - Receiving from a closed, empty channel returns zero value, ok=false // - Closing a nil channel panics // - Only the SENDER should close (receiver cannot know when sender is done)
Goroutine parking: when a send finds the buffer full (or an unbuffered channel has no receiver), the goroutine is added to the sendq (a linked list of goroutines in the hchan) and parked. When a receiver arrives, it directly copies the data from the parked sender's stack — a zero-copy optimisation for unbuffered channels — and unparks the sender goroutine.
More Related questions...