本筆記參考《Redis設計與實現》 P84~P88
內存回收
Redis在對象系統中使用reference counting技術實現了內存回收機制。程序可以通過跟蹤對象的引用計數信息,在適當的時候自動釋放對象并進行內存回收。
typedef struct redisObject {// ...// 引用計數int refcount;// ...
} robj;
refcount會隨著對象的使用狀態而不斷變化:
- 創建一個新對象時,refcount被初始化為1
- 當對象被一個新程序使用時,refcount++
- 當對象不再被一個程序使用時,refcount–
- 當對象引用計數為0時,對象所占內存被釋放
一些API:
函數 | 作用 |
---|---|
incrRefCount | 引用計數+1 |
decrRefCount | 引用計數-1 ,為0時釋放對象 |
resetRefCount | 引用計數設置為0,不釋放對象 |
舉例:
// 創建一個字符串對象s,對象引用計數為1
robj *s = createStringObject(...)
// 對象s執行各種操作
...
// 將對象s的引用計數-1,降為0,導致對象被釋放
decrRefCount(s)
對象共享
引用計數還帶有對象共享的作用。
在Redis中,讓多個鍵共享同一個值對象需要執行兩個步驟:
1、將數據庫鍵的值指向一個現有的值對象
2、將被共享的值對象的引用計數+1
共享對象機制對于節約內存非常有幫助,數據庫中保存的相同的值對象對越,對象共享機制就能節約越多的內存。
Redis會在初始化服務器時,創建一萬個字符串對象,包含了0~9999的所有整數值。當服務器有用到這些整數字符串對象,就利用的是共享對象,而非新創建對象。
可以使用OBJECT REFCOUNT 對象Key
來查看引用計數。
在數據結構中嵌套了字符串對象的獨享如(linkedlist編碼的列表對象,hashtable編碼的哈希對象,hashtable編碼的集合對象,zset編碼的有序集合對象)
需要注意下面一個問題:
只有共享對象和目標都西昂完全相同時,才會將共享對象的作為鍵的值對象,所以需要先驗證是否相等。
一個共享對象保存的值越復雜,驗證是否相等所需要的復雜度就越高:
1、如果是整數型字符串對象,O(1)
2、如果是字符串值的字符串對象,O(n)
3、如果是包含了多個對象的對象,O(n^2)
空轉時長
lru屬性記錄了對象最后一次被命令程序訪問的時間
typedef struct redisObject {// ...// 引用計數unsigned lur : 22;// ...
} robj;
使用OBJECT IDLETIME
可以打印出鍵的空轉時長(當前時間減去鍵的值對象的lru時間)。
如果服務器打開了maxmemory
選項,并且回收內存的算法為volatile-lru
或者allkeys-lru
,那么服務器占用的內存數超過maxmemory
設置的上限時,空轉時長較搞的那部分鍵會被服務器釋放。