https://blog.csdn.net/zhsenl/article/details/37565519
この記事senlieのオリジナル。:再現することは、このアドレス保管してくださいhttp://blog.csdn.net/zhengsenlieを
1.メモリ管理アーキテクチャ
Pythonのメモリ管理機構は、二つの実現がありますデバッグモードとモード解除
階層Pythonのメモリ管理機構:ティア0は、malloc関数などのオペレーティングシステムメモリ管理インターフェース、自由である第一の層のPythonでありますパッケージングがなさベースのオペレーティングシステムのメモリ管理インタフェースのレイヤ0。主にプラットフォームに関連付けられているメモリ割り当ての振る舞いに対処します。機能接頭辞PyMem_の家族達成するために設定されている関数やマクロ:2つのインターフェイス。マクロは関数呼び出しのオーバーヘッドを回避することができます。効率を向上させるが、C言語で書かれたPythonのと仮定Pythonのバイナリ非互換性の新バージョン生成することができる、拡張モジュールの機能インターフェースを使用するための良好なプログラミング方法である機能の2 PyObje_プレフィックスファミリー層です。Pythonオブジェクト・インターフェースを作成するための主要なプロバイダー。GCはメモリ管理含む層第三のオブジェクトバッファプールメカニズム2.メモリ空間の小さなプールメモリプール:Pymalloc機構。小さなメモリアプリケーションの管理とPyObject_Malloc、PyObject_Realloc、PyObject_Freeによって放出Pythonのために3つの表示インターフェースを全体小さなメモリのメモリ・プールは、階層と見なすことができます。四層に分け、底部から頂部にそれぞれ:ブロック、プール、アリーナ、メモリプールブロックブロック全体の長さは8バイト境界で整列される(アライメント)SMALL_REQUEST_THRESHOLD:メモリの種類を使用するPythonが可能で、この値はアプリケーション未満である場合メモリのブロックを満たすために
それは必要です。ときに、アプリケーションメモリサイズがこの制限を超えます。メモリ管理層への最初の要求を送信します。
そして、定義されてSMALL_REQUEST_THRESHOLDアラインメントに基づいて、次のような結論を得ることができ:
図。
//从size class index转换到size class
#define INDEX2SIZE(I) (((uint)(I) + 1) << ALIGNMENT_SHIFT)
//从size class到size class index
size = (uint )(nbytes - 1) >> ALIGNMENT_SHIFT;
プール
固定サイズ(ブロック)のメモリのプール管理パイルブロックを、ブロックのサイズは同じです。
システム・メモリ・ページは4キロバイトであり、現在のPythonのほとんどがサポートされているので、プールのサイズは、一般に、システム・メモリ・ページである
ので、内部Pythonはまた、プール4キロバイトのサイズとして定義され、このサイズは、ブロックpool_headerとプール管理を含みます
typedef uchar block;
struct pool_header{
union{ block *_padding;
uint count;} ref; //count表示已经分配的block数目
block *freeblock; //指向下一个可用 block
struct pool_header *nextpool; //链接下一个pool
struct pool_header *prevpool; //链接上一个pool
uint arenaindex;
uint szidx; //block 大小的index
uint nextoffset; //指向 freeblock之后的下一个可用的block
uint maxnextoffset; //指向了pool中最后一个可用的block距pool開始位置的偏移
};
最初のブロックから除去発行されたバイトのブロック管理などのメモリ・プール32の4キロバイト、およびブロック
#define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK)
#define POOL_OVERHEAD ROUNDUP(sizeof(struct pool_header))
#define struct pool_header *poolp
#define uchar block
poolp pool;
block *bp;
//pool指向了一块4KB的内存
pool->ref.count = 1;
//设置pool的size class index
pool->szidx = size;
//将size class index转换为size,比方3转换为32字节
size = INDEX2SIZE(size);
//跳过用于pool_header的内存,并进行对齐
bp = (block *)pool + POOL_OVERHEAD;
//实际就是pool->nextoffset = POOL_OVERHEAD+size + size
pool->nextoffset = POOL_OVERHEAD + (size << 1);
pool->maxnextoffset = POOL_size - size;
pool->freeblock = bp + size;
ときにアプリケーションメモリ、pool_headerの各ドメインの変更
if(pool != pool->nextpool){ ++pool->ref.count;
bp = pool->freeblock;
//...
if(pool->nextoffset <= pool->maxnextoffset){
//有足够的空间
pool->freeblock = (block *) pool + pool->nextoffset;
pool->nextoffset += INDEX2SIZE(size);
*(block **)(pool->freeblock) = NULL; //建立离散自由block链表的关键所在
return (void *)bp;
}
}
フリーブロックリスト
ブロックが解放されます
#define POOL_ADDR(P) ((poolp)((uptr)(p) & ~(uptr)POOL_SIZE_MASK))
void PyObject_Free(void *p){
poolp pool;
block *lastfree;
poolp next, prev;
uint size;
pool = POOL_ADDR(p);
//推断p指向的block是否属于pool
if(Py_ADDRESS_IN_RANGE(p, pool)){
*(block **)p = lastfree = pool->freeblock;//[1]
pool->freeblock = (block *)p;
//...
}
}
アリーナ
プールアリーナは、デフォルトのサイズは256キロバイトアリーナよりの集まりです
typedef uchar block;
struct arena_object{
uptr address;
block *pool_address;
uint nfreepools;
uint ntotalpools;
struct pool_header *freepools;
struct arena_object *nextarena;
struct arena_object *prevarena;
}
pool_headerメモリを管理し、自分自身をpool_header連続したメモリの一部です。メモリのarena_objectその管理は、孤立しています。 pool_headerが適用された場合は、メモリブロックコレクションがのために適用されている必要があります管理します。しかし、arena_objectが適用されたときに、 メモリはそれがプールはアプリケーションの集合ではありません管理します。一度接触を確立する必要があります。 arena_objectのアリーナは、プールのコレクションとの接触を確立していないとき。ここでは「未使用」の状態でアリーナ、一度確立接触、 その後、アリーナは、「利用可能」な状態に変換されます。 「未使用」アリーナリストヘッダがunused_arena_objectsあります。単独リンクリストされた 競技場の「利用可能」リストヘッダはusable_arenas、二重リストのリンクされている アプリケーションの分野を
//arenas管理着arena_object的集合 static struct arena_object *arenas = NULL; //当前arenas中管理的 arena_object的个数 static uint maxarenas = 0; //"未使用"的 arena_object链表 static struct arena_object *unused_arena_objects = NULL; //”可用“ 的 arena_object链表 static struct arena_object *usable_arenas = NULL; //初始化时需主持 arena_object的个数 #define INITIAL_ARENA_OBJECTS 16 static struct arena_object *new_arena(void){ struct arena_object *arenaobj; uint excess; //[1]:推断是否须要扩充”未使用“的 arena_object列表 if(unused_arena_objects == NULL){ uint i; uint numarenas; size_t nbytes; //[2]:确定本次须要申请的 arena_object的个数,并申请内存 numarenas = maxarenas ?
maxarenas << 1 : INITIAL_ARENA_OBJECTS; if(numarenas <= maxarenas) return NULL; //溢出 nbytes = numarenas * sizeof(*arenas); if(nbytes / sizeof(*arenas) != numarenas) return NULL; //溢出 arenaobj = (struct arena_object *)realloc(arenas, nbytes); if(arenaobj == NULL) return NULL; arenas = arenaobj; //[3]:初始化新申请的 arena_object。并将其放入 unused_arena_objects链表中 for(i = maxarrenas; i < numarenas; ++i){ arenas[i].address = 0; //mark as unassociated arenas[i].nextarena = i < numarenas - 1 ? &arenas[i + 1] : NULL; } //update globals unused_arena_objects = &arenas[maxarenas]; maxarenas = numarenas; } //[4]:从 unused_arena_objects 链表中取出一个“未使用”的arena_object arenaobj = unused_arena_objects; unused_arena_objects = arenaobj->nextarena; assert(arenaobj->address == 0); //[5]:申请arena_object管理的内存 arenaobj->address = (uptr)malloc(ARENA_SIZE); ++narenas_currently_allocated; //[6]:设置pool集合的相关信息 arenaobj->freepools = NULL; arenaobj->pool_address = (block *)arenaobj->address; arenaobj->nfreepools = ARENA_SIZE / POOL_SIZE; //将pool的起始地址调整为系统页的边界 excess = (uint)(arenaobj->address & POOL_SIZE_MASK); if(excess != 0){ --arenaobj->nfreepools; arenaobj->pool_address += POOL_SIZE - excess; } arenaobj->ntotalpools = arenaobj->nfreepools; return arenaobj; }
メモリ・プール
内部メモリおよび制御境界点256バイト(SMALL_REQUEST_THREADHOLD)におけるメモリのPythonのデフォルト小さなチャンク。
アプリケーションがメモリの256バイト未満であるとき。PyObject_Mallocはメモリプールメモリを適用する、アプリケーションメモリが256バイトよりも大きい場合。
PyObject_Mallocの動作はmallocの行動に退化します。
当Python申请内存时,最主要的操作单元是pool。由于pool的pool_head里有个szidx,表示block的大小。
一个pool在python执行的不论什么一个时刻,总处于下面三种状态的一种:
used状态、full状态、empty状态
处于used状态的pool被置于usedpools的控制之下。
//todo
3.循环引用的垃圾收集
Python 使用引用计数进行垃圾回收
长处:实时性。不论什么内存,一旦没有指向它的引用,就会马上被回收。为了与引用计数机制搭配。在内存的分配与释放上获得最高的效率,
Python设计了大量内存池机制。
缺点:循环引用。使得一组对象的引用计数都不为0。
- >ソリューション:ラベル-クリア、世代コレクションスリーカラーマーカーモデル1のルートセットオブジェクト(関数やグローバル参照スタックを引用)探し2ゴミ検出:ルートから設定されたオブジェクトは、オブジェクトのセットに沿ってルートを各参照は、それまでと呼ばれるオブジェクトAに到達すると想定される3ガベージコレクション:オブジェクト、オブジェクトを続けて到達不能削除にガベージコレクション4.Pythonをメインメモリ管理Pythonは参照であることを意味し循環参照の導入打破するために、メカニズムを数える「マーク-スイープ」と「世代収集する」だけでは循環参照を生成することがコンテナを持って、二重リンクリストに記録されているコンテナが作成されます通常のPythonオブジェクト:PyObject_HEAD +独自のデータ コンテナオブジェクトを:PyGc_Head + PyObject_HEAD +データ自体あなたが好きな科目になりたいコンテナオブジェクト、情報がPyGc_Headを増加させなければなりません
typedef union _gc_head{
struct{
union _gc_head *gc_next;
union _gc_head *gc_prev;
int gc_refs;
} gc;
long double dummy;
} PyGc_Head;
コンテナを作成するための最後のステップでは、このオブジェクトはリストにリンクされています
PyObject *PyDict_New(void){
register dictobject *mp;
//...
mp = PyObject_GC_New(dictobject, &PyDict_Type);
//...
_PyObject_GC_TRACK(mp);//将创建的container对象链接到了Python中的可收集对象链表中。
return (PyObject *)mp;
}
#define _PyObject_GC_TRACK(0) do { \
PyGc_Head *g = _Py_AS_GC(0); \
if(g->gc.gc_refs != _PyObject_GC_UNTRACKED) \
Py_FatalError("GC object already tracked"); \
g->gc.gc_refs = _PyGC_REFS_REACHABLE; \
g->gc.gc_next = _PyGC_generations0;
g->gc.gc_prev = _PyGC_generations0->gc.gc_prev; \
g->gc.gc_prev->gc.gc_prev = g;
_PyGC_generations0->gc.gc_prev = g;\
} while(0);
#define _PyObject_GC_UNTRACK(0) do{
//...
}
struct gc_generation{
PyGc_Head head;
int threshold; //最多可容纳的container对象,超出这个值会立马触发垃圾回收机制
int count; //已经链接的container对象数目
}
マーク内のPython -スイープ法
ガーベジコレクションを開始する前に、Pythonは完全にリストした後のメモリの最も「古い」世代の範囲外のカウント値にリンクされている「若い」世代のメモリリストになります。
サンプル のセットを見つけるために1ルートオブジェクト ルートオブジェクト:このオブジェクトへの参照では、オブジェクトへの参照がリスト外収集することができています。ルートオブジェクトに属する図のリスト1で唯一の16-16があります 参照カウントのコピーは- > gc.gc_refsにPyGc_Head 円形リング間オブジェクト参照の完全な除去をPyGc_Head gc.gc_refsに使用- >参照される各コンテナオブジェクトを介して、 1つのによって、その参照カウントを 、参照を除去した後のサイクルPyGC_Head.gc.gc_refsないルートコンテナオブジェクト0、オブジェクトに設定されている 2ガベージマーク 二つのリストの ルートリストを:オブジェクトルートオブジェクトルートオブジェクトと直接的または間接的に参照され 、到達不能リスト:ごみはオブジェクト 3.ガベージコレクションを ルートオブジェクトを見つけることで、オブジェクト間の循環参照を壊すためのプロセスをシミュレートするためにgc_refsを導入し、今は到達不能のリストまでの動作をob_refcntしたい 各オブジェクトは、オブジェクトを起こし、0となりob_refcnt破壊されました。 世代収集 時間空間:ディスカバリーシステムの異なるセットに分割し、それらの生存によれば、各セットは、ガベージコレクションの頻度を増加させる、「世代」と呼ばれ、生存時間の「世代」
減少。それは長い間、ターゲット、より多くの可能性がまだゴミを生きる、と言うことです。あまりあなたが収集するために行く必要があります。
Pythonの三の世代を採用します。代替リンクリストのメンテナンス。