這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
最近在做一個golang的串連池。測試過程中發現一個有趣的現象,擷取的串連沒有歸還給串連池,那麼過一段時間後該串連會自動關閉掉。猜測這跟串連池應該是沒有關係的,於是再用普通的串連做了實驗,即dial一個tcp串連,發送請求,然後程式進入sleep,一段時間後該串連還是會自動關閉。
對這個過程進行抓包分析,發現主動關閉串連的是client端,即client端主動向服務端發送了FIN包。
考慮到golang有記憶體回收機制,如果該串連被程式引用著怎麼辦?是否也會自動關閉?想想這應該是不可能的,那這是不是跟記憶體回收有關係?!
對程式稍作改動,在發送完第一個請求之後,保留該串連的reference不釋放,進入sleep。觀察一段時間,無論過多久,netstat查看該串連都是ESTABLISHED狀態的。
因此猜測這肯定跟GC有關係。
果然,在stackoverflow上發現golang的GC也會執行一個"Finalizer"的過程,跟Java類似。如果對象使用SetFinalizer()設定了Finalizer函數的話。預設情況下,有些對象是自動化佈建了Finalizer的:
os.File: The file is automatically closed when the object is garbage collected.
os.Process: Finalization will release any resources associated with the process. On Unix, this is a no-operation. On Windows, it closes the handle associated with the process.
On Windows, it appears that package net can automatically close a network connection.
答主指出了在Windows下,GC會關閉net串連,實際上在Unix平台下也是一樣的,有代碼為證:
func (fd *netFD) setAddr(laddr, raddr Addr) { fd.laddr = laddr fd.raddr = raddr runtime.SetFinalizer(fd, (*netFD).Close)}
這段代碼來自於golang源碼的net/fd_unix.go。如果這個fd是socket通訊端的話,便會調用該函數設定相應的Finalizer;而Finalizer函數即為netFD的Close()。
在net包裡,我們還可以看到,IPConn這個結構內部有個netFD類型的成員:
func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
至此我們已經明白了,為什麼程式中不再引用的串連會自動關閉了。