2016-07-21 44 views
9

我在Go中實現了一個HTTP服務器。如何在Golang中實現內存池

對於每個請求,我需要爲特定的結構創建數百個對象,並且我有〜10個這樣的結構。所以在請求按照執行完成之後,它將被垃圾收集。

因此,對於每個請求,這麼多的內存將被分配和釋放。

相反,我想要實現存儲池,以提高從分配端性能以及GC方也

在請求的開始,我會從池中,並把他們帶回請求送達後

從游泳池執行方

  1. 如何分配和釋放特定類型的結構的內存?
  2. 如何跟蹤這個內存分配的信息,而其他的不是?

有關在內存分配和釋放情況下提高性能的其他建議嗎?

+5

stdlib中有['sync.Pool'](https://golang.org/pkg/sync/#Pool)。除此之外,實施「免費清單」是一項並非特定於Go的技術。 – JimB

回答

14

注意事先:

許多建議使用sync.Pool這是臨時對象快速,良好的執行。但請注意,sync.Pool不保證彙集的對象被保留。從它的文檔引用:

存儲在池的任何項目可自動地在任何時候被除去而不通知。如果池在這種情況發生時只有唯一的引用,則該項可能會被釋放。

所以,如果你不想在Pool你的對象被垃圾收集(這取決於你的情況可能會導致更多的分配),下面提出的解決方案是更好,因爲在通道的緩衝值不垃圾收集。如果你的對象真的很大,那麼內存池是合理的,池通道的開銷將被攤銷。


最簡單的內存池「實現」是一個緩衝通道。

假設你想要一個大對象的內存池。創建一個緩衝通道,指向這些昂貴的對象的值,並且每當你需要時,從池(通道)接收一個。當你使用它時,把它放回池中(在頻道上發送)。爲避免意外丟失物品(例如發生恐慌),請在退回時使用defer聲明。

讓我們用這個作爲我們的大對象的類型:

type BigObject struct { 
    Id  int 
    Something string 
} 

創建池:

pool := make(chan *BigObject, 10) 

池的大小僅僅是通道的緩衝區的大小。

灌裝昂貴的對象的指針池(這是可選的,請在最後的筆記):

for i := 0; i < cap(pool); i++ { 
    bo := &BigObject{Id: i} 
    pool <- bo 
} 

很多夠程使用泳池:

wg := sync.WaitGroup{} 
for i := 0; i < 100; i++ { 
    wg.Add(1) 
    go func() { 
     defer wg.Done() 
     bo := <-pool 
     defer func() { pool <- bo }() 
     fmt.Println("Using", bo.Id) 
     fmt.Println("Releasing", bo.Id) 
    }() 
} 

wg.Wait() 

嘗試它的Go Playground

請注意,如果所有「池」對象都在使用,則此實現會阻止。如果你不希望這樣,您可以使用select強制創建新的對象,如果全部都在使用:

var bo *BigObject 
select { 
case bo = <-pool: // Try to get one from the pool 
default: // All in use, create a new, temporary: 
    bo = &BigObject{Id:-1} 
} 

在這種情況下,你不需要把它放回池中。或者你可以選擇嘗試與select把所有放回池中是否有足夠的空間在游泳池,不阻塞,再次:

select { 
case pool <- bo: // Try to put back into the pool 
default: // Pool is full, will be garbage collected 
} 

注:

事先灌裝池是可選的。如果您使用select嘗試從池中獲取/放回值,池可能最初爲空。

您必須確保您不會在請求之間泄漏信息,例如,請確保您不使用已設置並屬於其他請求的共享對象中的字段和值。

+5

注意這個方法比sync.Pool慢得多。 – OneOfOne

+3

@OneOfOne是的,但是'sync.Pool'不保證保留池值。見編輯的答案。 – icza

+0

好點,有道理。 – OneOfOne

10

這是@JimB提到的sync.Pool實現。請注意使用defer將對象返回給池。

package main 

import "sync" 

type Something struct { 
    Name string 
} 

var pool = sync.Pool{ 
    New: func() interface{} { 
     return &Something{} 
    }, 
} 

func main() { 
    s := pool.Get().(*Something) 
    defer pool.Put(s) 
    s.Name = "hello" 
    // use the object 
} 
+1

這不會創建對象實例創建的限制。 –