1、循环使用缓存
每条日志需要开辟缓存块来存储内容,以减少频繁的内存分配与回收。日志结构体定义如下:
type MLogger struct { // freeList is a list of byte buffers, maintained under freeListMu. freeList *buffer // freeListMu maintains the free list. It is separate from the main mutex // so buffers can be grabbed and printed to without holding the main lock, // for better parallelization. freeListMu sync.Mutex // ... }
已经开辟出的多个内存空间形成一个单链表,其中的freeList指向这个链表的头部。由于有多个go协程同时要操作这个单链表,如打印完日志后回收缓存,或者要求一个缓存块来存储日志内容。
首先看一下缓存的回收方法,如下::
// putBuffer returns a buffer to the free list. func (l *MLogger) putBuffer(b *buffer) { if b.Len() >= 1500 { fmt.Println("buffer:%d\n",b.Len()) return // Let big buffers die a natural death(自然死). } l.freeListMu.Lock() // 上锁 b.next = l.freeList // 为下一个可用的buffer设置值 l.freeList = b // 为当前可用的freeList设置buffer l.freeListMu.Unlock() // 解锁 }
当开辟的缓存块过大时不进行重复利用,以释放这些内存空间。
最主要的操作就是将不需要的缓存块插入到单链表的头部,然后让freeList指针指向新插入的缓存块即可。
获取缓存块:
// getBuffer returns a new, ready-to-use buffer. (获取一个新的,可使用的缓存) func (l *MLogger) getBuffer() *buffer { l.freeListMu.Lock() // 上锁 b := l.freeList if b != nil { l.freeList = b.next } l.freeListMu.Unlock() // 解锁 if b == nil { b = new(buffer) } else { b.next = nil b.Reset() } return b }
在获取缓存块时需要优先考虑freeList中可用的缓存块,如果有就从链表头部取一个块返回(注意:必须调用Reset()方法,因为这个块中还缓存有上一次日志的信息),否则就创建一个新的缓存块返回。