python原始碼分析----記憶體分配(2)
早就應該寫部分的內容了。。。。最近比較負能量。。。傷不起啊。。
上一篇說到了,在python的記憶體分配中兩個非常重要的方法:PyObject_Malloc和PyObject_Free
在具體的來這兩個方法之前,先要看看別的一些東西
//這裡用usedpool構成了一個雙向連結串列 //只用了兩個指標就搞定了。。我擦。。。 //這裡將儲存的地址減去了兩個指標的大小,那麼根據pool結構體的定義,那麼將地址加上兩個指標,正好就是next,加上3個指標正好就是prev //比較的巧妙 #define PTA(x) ( (poolp ) ((uchar *)&(usedpools[2*(x)]) - 2*sizeof(block *)) ) #define PT(x) PTA(x), PTA(x) static poolp usedpools[2 * ((NB_SMALL_SIZE_CLASSES + 7) / 8) * 8] = { PT(0), PT(1), PT(2), PT(3), PT(4), PT(5), PT(6), PT(7) #if NB_SMALL_SIZE_CLASSES > 8 , PT(8), PT(9), PT(10), PT(11), PT(12), PT(13), PT(14), PT(15) #if NB_SMALL_SIZE_CLASSES > 16 , PT(16), PT(17), PT(18), PT(19), PT(20), PT(21), PT(22), PT(23) #if NB_SMALL_SIZE_CLASSES > 24 , PT(24), PT(25), PT(26), PT(27), PT(28), PT(29), PT(30), PT(31) #if NB_SMALL_SIZE_CLASSES > 32 , PT(32), PT(33), PT(34), PT(35), PT(36), PT(37), PT(38), PT(39) #if NB_SMALL_SIZE_CLASSES > 40 , PT(40), PT(41), PT(42), PT(43), PT(44), PT(45), PT(46), PT(47) #if NB_SMALL_SIZE_CLASSES > 48 , PT(48), PT(49), PT(50), PT(51), PT(52), PT(53), PT(54), PT(55) #if NB_SMALL_SIZE_CLASSES > 56 , PT(56), PT(57), PT(58), PT(59), PT(60), PT(61), PT(62), PT(63) #if NB_SMALL_SIZE_CLASSES > 64 #error "NB_SMALL_SIZE_CLASSES should be less than 64" #endif /* NB_SMALL_SIZE_CLASSES > 64 */ #endif /* NB_SMALL_SIZE_CLASSES > 56 */ #endif /* NB_SMALL_SIZE_CLASSES > 48 */ #endif /* NB_SMALL_SIZE_CLASSES > 40 */ #endif /* NB_SMALL_SIZE_CLASSES > 32 */ #endif /* NB_SMALL_SIZE_CLASSES > 24 */ #endif /* NB_SMALL_SIZE_CLASSES > 16 */ #endif /* NB_SMALL_SIZE_CLASSES > 8 */ };
前面我們就應該可以知道了,pool其實是在arena結構上分配的,但是pool並不是由arena管理,而且前面我們已經知道pool其實是有型別的,每一種型別的pool只用來分配固定大小的記憶體,例如,pool的szidx編號如果是0,那麼它就是用來分配8位元組記憶體的。。。
好了,pool是在arena上面分配,但是並不是由他來管理,那麼是由什麼東西來管理的呢。。。?上面貼出來的程式碼定義了一個usedpools陣列,嗯,就是用它來定義的。。。
而且,每種型別的pool都構成了一個雙鏈表,這個陣列就儲存了這個雙鏈表的頭部。。。
呵呵,沒有搞錯吧,這個事怎麼實現的呢。。?這個實現真的時夠trick的,我也是看了很久才理解到,真是才疏學淺,以前還真沒有見過這樣子的實現。。。。
有前面的巨集PTA和PT就可以知道,PT(0)其實是定義了兩個指標,而且儲存的值正好是第一個第一個指標的地址再減去兩個指標的地址,嗯,再看看pool的定義:
struct pool_header { union { block *_padding; uint count; } ref; /* number of allocated blocks */ //當前pool上面分配的block的數量 block *freeblock; /* pool's free list head */ //指向下一個可用的block,這裡構成了一個連結串列, 它是一個離散的連結串列,很有意思 struct pool_header *nextpool; /* next pool of this size class */ //通過這兩個指標形成pool的雙鏈表 struct pool_header *prevpool; /* previous pool "" */ uint arenaindex; /* index into arenas of base adr */ //在arena裡面的索引 uint szidx; /* block size class index */ //分配記憶體的類別,8位元組,16或者。。。 uint nextoffset; /* bytes to virgin block */ //下一個可用的block的記憶體偏移量 uint maxnextoffset; /* largest valid nextoffset */ //最後一個block距離開始位置的距離 }; typedef struct pool_header *poolp; //頭部
嗯,看看加上兩個指標大小是不是正好指向nextpool指標,加上三個指標大小是不是正好指向prevpool指標。。。這裡的實現就不多說啦。。其實上面的註釋也解釋了一些東西。。。如果不能理解,那就自己再好好看。。這個過程一定是要自己經過努力去理解才好。。。。。
那麼接下來來看看PyObject_Malloc的定義吧:
void *
PyObject_Malloc(size_t nbytes)
{
block *bp; //將會用這個指標指向分配的記憶體地址
poolp pool;
poolp next;
uint size;
#ifdef WITH_VALGRIND
if (UNLIKELY(running_on_valgrind == -1))
running_on_valgrind = RUNNING_ON_VALGRIND;
if (UNLIKELY(running_on_valgrind))
goto redirect;
#endif
/*
* Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
* Most python internals blindly use a signed Py_ssize_t to track
* things without checking for overflows or negatives.
* As size_t is unsigned, checking for nbytes < 0 is not required.
*/
if (nbytes > PY_SSIZE_T_MAX) //這個難道 都會出現。。?
return NULL;
/*
* This implicitly redirects malloc(0).
*/
if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { //如果這裡可以用小地址分配模式
LOCK();
/*
* Most frequent paths first
*/
//其實這裡計算的時當前nbytes是8的幾倍,這裡從0開始計數
//例如8的話,就是0,9就是1,16為1,17為2,主要是用來確定當前這個記憶體應該在哪一個pool裡面進行分配
//因為每種pool都是分配固定大小的記憶體的
size = (uint)(nbytes - 1) >> ALIGNMENT_SHIFT; //ALIGNMENT_SHIFT=3
pool = usedpools[size + size]; //獲取當前型別pool的雙鏈表的頭部
if (pool != pool->nextpool) { //這裡說明當前下面有可用的pool
/*
* There is a used pool for this size class.
* Pick up the head block of its free list.
*/
++pool->ref.count; //當前分配的block的數量+1
bp = pool->freeblock; //將bp指標指向當前的freeblock,也就是分配的記憶體就是這裡了
assert(bp != NULL);
//這裡將freeblock的值賦值為原來指向的那塊地址裡面存的值,其實哪裡存的是下一個可用的block的地址
//這裡就可以理解為將連結串列指向下一個可用的block
if ((pool->freeblock = *(block **)bp) != NULL) {
//走到了這裡,說明下面都還有可用的block,那麼可以直接返回了
UNLOCK();
return (void *)bp;
}
//到這裡說明了當前已經到了freeblock連結串列的尾部,那麼嘗試一下擴充套件當前的連結串列
if (pool->nextoffset <= pool->maxnextoffset) {
/* There is room for another block. */
//當前pool還有記憶體可以分配,那麼將freeblock指向下一個可用的地址
pool->freeblock = (block*)pool +
pool->nextoffset;
pool->nextoffset += INDEX2SIZE(size); // 更新nextoffset值
*(block **)(pool->freeblock) = NULL; // 將下一個可用地址的值設定為NULL,表示它是尾部
UNLOCK();
return (void *)bp;
}
/* Pool is full, unlink from used pools. */
//程式碼執行到了這裡,說明整個pool都與已經用完了,那麼將這個pool從usedpool裡面移除出去
next = pool->nextpool;
pool = pool->prevpool;
next->prevpool = pool;
pool->nextpool = next;
UNLOCK();
return (void *)bp;
}
/* There isn't a pool of the right size class immediately
* available: use a free pool.
*/
//走到這裡,說明當前已經沒有這種型別的pool可以分配記憶體了
//那麼接下里建立一個pool
if (usable_arenas == NULL) { //若果沒有可用的arena,那麼獲取一個
/* No arena has a free pool: allocate a new arena. */
#ifdef WITH_MEMORY_LIMITS
if (narenas_currently_allocated >= MAX_ARENAS) {
UNLOCK();
goto redirect;
}
#endif
usable_arenas = new_arena();
if (usable_arenas == NULL) {
UNLOCK();
goto redirect;
}
usable_arenas->nextarena =
usable_arenas->prevarena = NULL;
}
assert(usable_arenas->address != 0);
/* Try to get a cached free pool. */
//從arena裡面建立一個pool
pool = usable_arenas->freepools;
if (pool != NULL) {
/* Unlink from cached pools. */
//將arena的freepool指向下一個
usable_arenas->freepools = pool->nextpool;
/* This arena already had the smallest nfreepools
* value, so decreasing nfreepools doesn't change
* that, and we don't need to rearrange the
* usable_arenas list. However, if the arena has
* become wholly allocated, we need to remove its
* arena_object from usable_arenas.
*/
--usable_arenas->nfreepools; //可分配的pool的數量減1
if (usable_arenas->nfreepools == 0) {
//走到這裡,說明這個arena已經將所有的記憶體都分配完了,那麼將這個arena結構從usable_arenas移除
/* Wholly allocated: remove. */
assert(usable_arenas->freepools == NULL);
assert(usable_arenas->nextarena == NULL ||
usable_arenas->nextarena->prevarena ==
usable_arenas);
usable_arenas = usable_arenas->nextarena;
if (usable_arenas != NULL) {
usable_arenas->prevarena = NULL;
assert(usable_arenas->address != 0);
}
}
else {
/* nfreepools > 0: it must be that freepools
* isn't NULL, or that we haven't yet carved
* off all the arena's pools for the first
* time.
*/
assert(usable_arenas->freepools != NULL ||
usable_arenas->pool_address <=
(block*)usable_arenas->address +
ARENA_SIZE - POOL_SIZE);
}
init_pool:
/* Frontlink to used pools. */
//初始化當前建立的pool,然後將其插入到其對應的usedpool儲存的雙鏈表上面去
//獲取當前大小pool的雙向連結串列的頭指標,更新當前的雙鏈表
next = usedpools[size + size]; /* == prev */
pool->nextpool = next;
pool->prevpool = next;
next->nextpool = pool;
next->prevpool = pool;
pool->ref.count = 1; //這裡將分配的block的數量設定為1
if (pool->szidx == size) { //這裡說明這個pool以前就是按當前大小分配過,就直接接著分配記憶體就好了
/* Luckily, this pool last contained blocks
* of the same size class, so its header
* and free list are already initialized.
*/
bp = pool->freeblock;
pool->freeblock = *(block **)bp; //下一個可用的block的地址
UNLOCK();
return (void *)bp;
}
/*
* Initialize the pool header, set up the free list to
* contain just the second block, and return the first
* block.
*/
//走到這裡,說明這個pool都才剛剛開始用,那麼先進行一下頭部的初始化
pool->szidx = size;
size = INDEX2SIZE(size); //獲取需要分配的記憶體大小
bp = (block *)pool + POOL_OVERHEAD; //這裡需要跳過頭部需要的記憶體,這裡可以理解為頭部也是儲存在arena申請的記憶體裡面
pool->nextoffset = POOL_OVERHEAD + (size << 1);
pool->maxnextoffset = POOL_SIZE - size; //這裡設定最後一個可用的block的偏移量
pool->freeblock = bp + size; //下一個可用的block的首地址
*(block **)(pool->freeblock) = NULL; //通過設定null,表示這個freeblock之後沒有連結的可用block,記住,這裡形成了一個離散的連結串列
UNLOCK();
return (void *)bp;
}
/* Carve off a new pool. */
//在arena裡面沒有可用的pool,一開始建立的arena是沒有freepool的,因為都沒有分配
//等到分配的freepool又完全空閒了之後,才會將其放到freepool連結串列裡面
assert(usable_arenas->nfreepools > 0);
assert(usable_arenas->freepools == NULL);
pool = (poolp)usable_arenas->pool_address; //這裡可以理解為分配一個pool
assert((block*)pool <= (block*)usable_arenas->address +
ARENA_SIZE - POOL_SIZE);
pool->arenaindex = usable_arenas - arenas; //當前pool所屬的arena是第幾個
assert(&arenas[pool->arenaindex] == usable_arenas);
pool->szidx = DUMMY_SIZE_IDX; //通過這個標誌來表示當前這個pool其實還沒有進行分配過,待會在這個pool上面進行資料分配的時候就知道先初始化一下頭部了
usable_arenas->pool_address += POOL_SIZE; //將下一個可用的pool地址向下移動
--usable_arenas->nfreepools; //可分配pool的數量減一
//表示在當前arena裡面已經沒法分配新的pool了,所以這裡更新usable_arenas指向下一個
if (usable_arenas->nfreepools == 0) {
assert(usable_arenas->nextarena == NULL ||
usable_arenas->nextarena->prevarena ==
usable_arenas);
/* Unlink the arena: it is completely allocated. */
usable_arenas = usable_arenas->nextarena;
if (usable_arenas != NULL) {
usable_arenas->prevarena = NULL;
assert(usable_arenas->address != 0);
}
}
//接下來初始化pool
goto init_pool;
}
/* The small block allocator ends here. */
redirect:
/* Redirect the original request to the underlying (libc) allocator.
* We jump here on bigger requests, on error in the code above (as a
* last chance to serve the request) or when the max memory limit
* has been reached.
*/
if (nbytes == 0)
nbytes = 1;
return (void *)malloc(nbytes);
}
基本上內容在上面程式碼的註釋應該說的很清楚了吧。。。這裡另外有一個很有意思的地方就是pool通過freeblock指標來實現了一個離散的單鏈表,釋放的block將會放到這個單鏈表的頭部。。。。至於這個單鏈表是怎麼實現的。。。要看一下PyObject_Free的實現才好說。。。。
那麼接下來又來看看它的實現吧:
void
PyObject_Free(void *p)
{
poolp pool; //用這個指標指向當前要釋放的記憶體所在的pool的地址
block *lastfree;
poolp next, prev;
uint size;
#ifndef Py_USING_MEMORY_DEBUGGER
uint arenaindex_temp;
#endif
if (p == NULL) /* free(NULL) has no effect */
return;
#ifdef WITH_VALGRIND
if (UNLIKELY(running_on_valgrind > 0))
goto redirect;
#endif
//通過這巨集來找到當前這個block所屬的pool的地址,因為在arena裡面已經保證了pool的地址
//肯定是4KB的整數倍,所以通過尋找前面最近的4KB整數倍地址就好了
pool = POOL_ADDR(p);
if (Py_ADDRESS_IN_RANGE(p, pool)) {
/* We allocated this address. */
LOCK();
/* Link p to the start of the pool's freeblock list. Since
* the pool had at least the p block outstanding, the pool
* wasn't empty (so it's already in a usedpools[] list, or
* was full and is in no list -- it's not in the freeblocks
* list in any case).
*/
assert(pool->ref.count > 0); /* else it was empty */
//下面是構建freeblock連結串列的關鍵,將當要釋放的記憶體,用它來儲存當前的freelock,這樣也就構成了一個連結串列
//也就是將當前釋放的block放到可用blcok連結串列的頭部
*(block **)p = lastfree = pool->freeblock; //這裡將當前P指向上的地址賦值為freelock的地址,也就構成了離散的連結串列
pool->freeblock = (block *)p; //將freeblock指向當前釋放的地址,相當於將當前可用block連結串列的頭部指向這裡
if (lastfree) {
//走到這裡,說明在回收這次記憶體之前,pool就已經有空閒的空間
//接下來要做的事情主要是處理一下當前pool空了的情況
struct arena_object* ao;
uint nf; /* ao->nfreepools */
/* freeblock wasn't NULL, so the pool wasn't full,
* and the pool is in a usedpools[] list.
*/
if (--pool->ref.count != 0) { //如果當前pool還是有分配出去的block,那麼繼續將其放在usedpools連結串列裡面
/* pool isn't empty: leave it in usedpools */
UNLOCK();
return;
}
/* Pool is now empty: unlink from usedpools, and
* link to the front of freepools. This ensures that
* previously freed pools will be allocated later
* (being not referenced, they are perhaps paged out).
*/
//走到這裡,說明當前這個pool已經是空的了,那麼將這個pool從其所在的usedpool的雙鏈表裡面移除
next = pool->nextpool;
prev = pool->prevpool;
next->prevpool = prev;
prev->nextpool = next;
/* Link the pool to freepools. This is a singly-linked
* list, and pool->prevpool isn't used there.
*/
//接下來將這個pool的空間還給其所在的arena,這裡將當前的pool放到arena的可用pool連結串列的頭部
ao = &arenas[pool->arenaindex];
pool->nextpool = ao->freepools;
ao->freepools = pool;
nf = ++ao->nfreepools; // 將當前arena的可分配的pool的數量+1
/* All the rest is arena management. We just freed
* a pool, and there are 4 cases for arena mgmt:
* 1. If all the pools are free, return the arena to
* the system free().
* 2. If this is the only free pool in the arena,
* add the arena back to the `usable_arenas` list.
* 3. If the "next" arena has a smaller count of free
* pools, we have to "slide this arena right" to
* restore that usable_arenas is sorted in order of
* nfreepools.
* 4. Else there's nothing more to do.
*/
/**
(1)如果當前arena所有的pool都已經空了,那麼將為這個arena分配的256KB釋放掉
(2)如果當前這個釋放的pool是當前arena唯一的空閒的pool,那麼將這個arena放到usable_arenas連結串列的頭部
(3) 如果後面的arena可用的pool數目比當前arena還要少,那麼將他們放到這個arena的頭部,也就是修改連結串列,按可用pool數目的升序排序
**/
if (nf == ao->ntotalpools) { //這裡說明當前arena已經沒有分配任何pool了
//那麼接下來要做的事情是將其移除
/* Case 1. First unlink ao from usable_arenas.
*/
assert(ao->prevarena == NULL ||
ao->prevarena->address != 0);
assert(ao ->nextarena == NULL ||
ao->nextarena->address != 0);
/* Fix the pointer in the prevarena, or the
* usable_arenas pointer.
*/
if (ao->prevarena == NULL) {
usable_arenas = ao->nextarena;
assert(usable_arenas == NULL ||
usable_arenas->address != 0);
}
else {
assert(ao->prevarena->nextarena == ao);
ao->prevarena->nextarena =
ao->nextarena;
}
/* Fix the pointer in the nextarena. */
if (ao->nextarena != NULL) {
assert(ao->nextarena->prevarena == ao);
ao->nextarena->prevarena =
ao->prevarena;
}
/* Record that this arena_object slot is
* available to be reused.
*/
//將當前arena結構放到unused_arena_objects裡面去
ao->nextarena = unused_arena_objects;
unused_arena_objects = ao;
/* Free the entire arena. */
//釋放為這個arena分配的256KB的記憶體
#ifdef ARENAS_USE_MMAP
munmap((void *)ao->address, ARENA_SIZE);
#else
free((void *)ao->address);
#endif
ao->address = 0; /* mark unassociated */
--narenas_currently_allocated;
UNLOCK();
return;
}
if (nf == 1) { //當前可供分配的pool數量為1
/* Case 2. Put ao at the head of
* usable_arenas. Note that because
* ao->nfreepools was 0 before, ao isn't
* currently on the usable_arenas list.
*/
//那麼這個arena放到usable_arenas的頭部去
ao->nextarena = usable_arenas;
ao->prevarena = NULL;
if (usable_arenas)
usable_arenas->prevarena = ao;
usable_arenas = ao;
assert(usable_arenas->address != 0);
UNLOCK();
return;
}
/* If this arena is now out of order, we need to keep
* the list sorted. The list is kept sorted so that
* the "most full" arenas are used first, which allows
* the nearly empty arenas to be completely freed. In
* a few un-scientific tests, it seems like this
* approach allowed a lot more memory to be freed.
*/
//如果當前arena的下一個arena為空,或者當前arena空閒的pool數量小於
//下一個arena的數目,那麼保持它的位置
if (ao->nextarena == NULL ||
nf <= ao->nextarena->nfreepools) {
/* Case 4. Nothing to do. */
UNLOCK();
return;
}
/* Case 3: We have to move the arena towards the end
* of the list, because it has more free pools than
* the arena to its right.
* First unlink ao from usable_arenas.
*/
//走到這裡說明當前arena的next還有arena,而且它們空閒的pool的數目更少
if (ao->prevarena != NULL) {
/* ao isn't at the head of the list */
assert(ao->prevarena->nextarena == ao);
ao->prevarena->nextarena = ao->nextarena;
}
else {
/* ao is at the head of the list */
assert(usable_arenas == ao);
usable_arenas = ao->nextarena;
}
ao->nextarena->prevarena = ao->prevarena;
/* Locate the new insertion point by iterating over
* the list, using our nextarena pointer.
*/
//將當前arena移動到其改在的位置上,這裡按照可分配pool的數目的升序
while (ao->nextarena != NULL &&
nf > ao->nextarena->nfreepools) {
ao->prevarena = ao->nextarena;
ao->nextarena = ao->nextarena->nextarena;
}
/* Insert ao at this point. */
assert(ao->nextarena == NULL ||
ao->prevarena == ao->nextarena->prevarena);
assert(ao->prevarena->nextarena == ao->nextarena);
ao->prevarena->nextarena = ao;
if (ao->nextarena != NULL)
ao->nextarena->prevarena = ao;
/* Verify that the swaps worked. */
assert(ao->nextarena == NULL ||
nf <= ao->nextarena->nfreepools);
assert(ao->prevarena == NULL ||
nf > ao->prevarena->nfreepools);
assert(ao->nextarena == NULL ||
ao->nextarena->prevarena == ao);
assert((usable_arenas == ao &&
ao->prevarena == NULL) ||
ao->prevarena->nextarena == ao);
UNLOCK();
return;
}
/* Pool was full, so doesn't currently live in any list:
* link it to the front of the appropriate usedpools[] list.
* This mimics LRU pool usage for new allocations and
* targets optimal filling when several pools contain
* blocks of the same size class.
*/
//走到這裡,說明以前這個pool已經滿了,那麼釋放了一個之後就有新的空間了,
//那麼將這個pool再次放入到pool的連結串列裡面
--pool->ref.count; //已經分配的block的數目減去1
assert(pool->ref.count > 0); /* else the pool is empty */
size = pool->szidx;
next = usedpools[size + size]; //獲取當前szidx型別的pool的連結串列頭部
prev = next->prevpool;
/* insert pool before next: prev <-> pool <-> next */
//將當前這個pool插入到連結串列裡面
pool->nextpool = next;
pool->prevpool = prev;
next->prevpool = pool;
prev->nextpool = pool;
UNLOCK();
return;
}
#ifdef WITH_VALGRIND
redirect:
#endif
/* We didn't allocate this address. */
free(p);
}
這個,其實程式碼應該很好理解,多了很多的判斷,pool是否空,arena是否已經空了,然後調整arena的順序啥的。。。
另外就是上面提到的freeblock的單鏈表,怎麼實現的。。。其實好好看看應該就能理解的。。。這裡就不細說了。。。好多東西還是要自己看懂才好。。。
到這裡,python一些記憶體分配的東西也算有了大致的瞭解。。。。
相關推薦
python原始碼分析----記憶體分配(2)
早就應該寫部分的內容了。。。。最近比較負能量。。。傷不起啊。。 上一篇說到了,在python的記憶體分配中兩個非常重要的方法:PyObject_Malloc和PyObject_Free 在具體的來這兩個方法之前,先要看看別的一些東西 //這裡用usedpool構成了一個雙
類和動態記憶體分配(2)
·## 改進後的StringBad類: //stringbad.h #include <iostream> #ifndef STRINGBAD_H_ #define STRINGBAD_H_ class StringBad { private : char *str;
Netty原始碼分析--記憶體模型(上)(十一)
前兩節我們分別看了FastThreadLocal和ThreadLocal的原始碼分析,並且在第八節的時候講到了處理一個客戶端的接入請求,一個客戶端是接入進來的,是怎麼註冊到多路複用器上的。那麼這一節我們來一起看下客戶端接入完成之後,是怎麼實現讀寫操作的?我
Netty原始碼分析--記憶體模型(下)(十二)
這一節我們一起看下分配過程 1 PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) { 2 PooledByteBuf
02.1跟雨痕看go原始碼-記憶體分配(分配&回收)
記憶體分配和垃圾回收有關,這裡我們可以先看一下記憶體分配。 垃圾回收比較複雜,後面講。 一開始雨痕大大說了幾個基本策略: 每次從作業系統申請一大塊記憶體(比如1MB),以減少系統呼叫。 將申請到的大塊記憶體按預定大小預先切分成小塊,構成連結串列。 為物件
Linux核心原始碼分析--記憶體管理(一、分頁機制)
Linux系統中分為幾大模組:程序排程、記憶體管理、程序通訊、檔案系統、網路模組;各個模組之間都有一定的聯絡,就像蜘蛛網一樣,所以這也是為什麼Linux核心那麼難理解,因為不知道從哪裡開始著手去學習。很多人會跟著系統上電啟動 BIOS-->bootse
結合redis設計與實現的redis原始碼學習-1-記憶體分配(zmalloc)
在進入公司後的第一個任務就是使用redis的快取功能實現伺服器的雲託管功能,在瞭解了大致需求後,依靠之前對redis的瞭解封裝了常用的redis命令,並使用單例的連線池來維護與redis的連線,使用連線池來獲取redis的連線物件,依靠這些功能基本可以實現要求的
python 分析nginx日誌(2)
閒著也是閒著,練練手,分析nginx日誌http code碼簡訊告知 #!/usr/bin/python # -*- coding: UTF-8 -*- ''' auth yufei 2016-4-3 分析nginx日誌,計算200以及500等個數,並且簡訊
Linux核心原始碼分析--記憶體管理(二、函式實現技巧)
仔細的分析了一下各個記憶體管理函式的實現,發現裡面涉及到了幾個技巧,如果知道了這幾個技巧,那麼閱讀記憶體管理原始碼將會事半功倍(主要是這幾個技巧在幾個函式中都出現過),當然也會選擇性的分析幾個比較重要的函式實現; 函式實現技巧 1、向上取整
HDFS源碼分析之NameNode(2)————Format
return exceptio 數據信息 row oid creat tail 進行 alt 在Hadoop的HDFS部署好了之後並不能馬上使用,而是先要對配置的文件系統進行格式化。在這裏要註意兩個概念,一個是文件系統,此時的文件系統在物理上還不存在,或許是網絡磁盤來
python導入模塊(2)
line -h .com pre () main alt class print main.py文件 #_*_coding:utf-8_*_ #__author__ = "csy" from module_csy import say_hello as hello
Python+selenium之測試報告(2)
def window win get hot 過程 https 書寫 screen 1 # -*- coding: utf-8 -*- 2 import HTMLTestReport 3 import HTMLTestRunner 4 import os 5 i
船長帶你看書——《selenium2 python 自動化測試實戰》(2)瀏覽器操作
python lin div 看書 名稱 ext ice 微信公眾號 很難 瀏覽器操作 # coding: utf-8 from selenium import webdriver from time import sleep driver = webdriver.Fi
27. Python對Mysql的操作(2)
python mysql1.遊標遊標是系統為用戶開設的一個數據緩沖區,存放SQL語句的執行結果用戶可以用SQL語句逐一從遊標中獲取記錄,並賦給主變量,交由python進一步處理,一組主變量一次只能存放一條記錄僅使用主變量並不能完全滿足SQL語句向應用程序輸出數據的要求遊標提供了一種對從表中檢索出的數據進行操作
57. Python saltstack 二次開發(2)
http協議 class 方式 clas 調用 官網 創建 分享 tex 回顧上一節:grains 和 pillar 都是定義他們的屬性的grains 定義在minion端(定義完必須重啟minion,才能生效)pillar 定義在master端(無需重啟即可生效)sal
【AI基礎】python:openCV——圖像處理(2)
getTrackbarPos圖像處理練習 制作一個滑動條調色板,使用函數cv2.getTrackbarPos();cv2.creatTrackbar() import cv2 import numpy as np def nothing(x): pass img = np.zeros((300,5
Python第一周 學習筆記(2)
學習筆記習題解析 0.打印10以內偶數:位運算 for i in range(10): if not i & 0x01: print(i) 1.給定一個不超過5位的正整數,判斷其有幾位(使用input函數) 方法一:正常邏輯處理 a = int(input("Please e
進階第一課 Python內置函數(2)
其他 課程 最小 col 使用 post 示例 float pre 1、float() 上一課中,我們可以使浮點數變為整數;相反的也可以把整數變為浮點數。看示例: >>> a=10 >>> b=float(10) >>>
Day2----Python學習之路筆記(2)
cell 數據類型的轉換 編碼格式 python3 () shel 不能 索引 png 學習路線: Day1 Day2 Day3 Day4 Day5 ...待續 一、簡單回顧一下昨天的內容 1. 昨天了解到了一些編碼的知識 1.1
初學python的一些簡單程序(2)
eric not in mov AS int pen != 必須 light 1)判斷兩個列表內容相同的元素 l1=[11,22,33] l2=[22,33,44] for i in l1: if i in l2: print(i) 2)獲取l1