1. 程式人生 > >golang的垃圾回收與Finalizer——tcp連線是如何被自動關閉的

golang的垃圾回收與Finalizer——tcp連線是如何被自動關閉的

      最近在做一個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}} }

      至此我們已經明白了,為什麼程式中不再引用的連線會自動關閉了。