Pythonのメモリと数字

こんにちは、すべて。



以前はCに似た言語で作業していましたが、今はPythonで座っていました。 構文は簡単で、難しい質問に変わりました。 猫の下-Pythonがメモリにデータストレージを実装する方法についての記事。 私は本当のふりをしませんが、それを理解しようとします。



リンクを見る



最も単純なものから始めましょう。 Pythonのデータはオブジェクトであり、変数はオブジェクトへの参照です。 オブジェクトではないデータはありません。 まず、2つの「同一の」オブジェクトが同一であるかどうかを判断する必要があります。 これを行うには、アドレスを取得する必要があります。これにより、組み込みのid()関数を簡単に作成できます。 私たちは試します:



print(id(0))
      
      





予想どおり、判読できないものが出力されます。 多数は、おそらく実際のアドレスです。 ただし、全体で使用される各数値がメモリに格納されている場合、自然に十分なメモリはありません。 短い実験が行われています:



 print(id(0)) print(id(0))
      
      





2つのまったく同じ番号。 したがって、すべての定数は実際には重複することなくメモリに格納されます。 それは論理的です-Pythonはすでに生産性が低いので、このようなトリックを使用すると、最後の残りを保存できます。 さて、すべてのメモリをゼロの巨大な配列で埋めてみましょう。



 a = [0] while True: a += [0]
      
      





予想通り、無限のサイクルは無限に実行されますが、メモリはほとんど必要ありません。 別の実験:



 a = [0, 0] print(id(a[0])) print(id(a[1]))
      
      





まあ、はい、同じ数字です。 むしろ、確認するために、2つの異なる変数で同じチェックを行っています。同じ番号で、id(0)に等しいものです。 つまり、アルゴリズムは明らかにこれです。変数値が変更されると、メモリ内で同じかどうかを確認し、そうであれば、リンクをリダイレクトします。 オブジェクトはかなりのメモリを占有するため、この動作が必要です。さらにコンパクトにするために、Pythonは既存のオブジェクトを最大限に活用します。 記事をコードで煩雑にしないために、文字列(スライスから取得したものを含む)、論理オブジェクト、さらには配列についても、これは同じように機能すると言います。 Pythonですべてのメモリを取得するために2回目の試行を行います。



 i = 0 a = [0] while True: a += [a[i]] i += 1
      
      





成功! メモリ消費量は常に増加しています。 最初の結論を導きます:

1. Pythonのデータはすべてオブジェクトです。

2.オブジェクトが「同じ」場合、それらはメモリ内の同じアドレスに保存されます。 つまり、a == bとid(a)== id(b)は同等のステートメントです。

3.これ以上複雑な最適化は使用されません-配列内のかなり単純な依存関係はもはや最適化されません(ルール「a [i] = i」のみ)。 しかし、それを使用する場合、私は驚くでしょう:ここではかなり複雑な字句解析が必要であり、Pythonはステップバイステップの解釈では余裕がありません。

リンクを数える



免責事項:Pythonインタラクティブモードで作業するようになりました。 オブジェクトへの参照をカウントするために、sys.getrefcount()関数があります。 システムのインポート:



 >>> from os import sys
      
      





そして、まず第一に、それが生成するデータがどれほど現実のものかを判断する必要があります。



 >>> sys.getrefcount('There is no this string in Python') 3 >>> sys.getrefcount('9695c3716e3b801367b7eca6a3281ac9') #md5- 512    /dev/urandom. 3 >>> a = 'More random for the random god!' >>> sys.getrefcount(a) 2 >>> a = 0 >>> sys.getrefcount(a) 434 >>> sys.getrefcount(0) 436
      
      





これは1つの面白いことを教えてくれます。リンクをカウントし、getrefcount()がそれらを自分で作成します。 ご覧のとおり、定数については2つ作成します(実際2つ、大量の入力データを試してみましたが、ここでは不要なものとして公開しません)。したがって、2つを差し引くことができます。 、ただし変数自体は考慮しません。 さて、我々は現実からの結果の逸脱を理解しました。 次に、いくつかの例を示します。



 >>> sys.getrefcount(1) 754 >>> sys.getrefcount(65) 13 >>> sys.getrefcount(67) 11 >>> sys.getrefcount('A') 4 >>> sys.getrefcount('a') 6 >>> sys.getrefcount(False) 100 >>> sys.getrefcount(True) 101 #  !
      
      





ポインターが突然青色(単位あたり751個の整数)から突然表示されるのはなぜですか? この関数はCポインターをカウントするため、つまり、Pythonコード自体で使用されるものが含まれます。 実際、私たちは開発者が私たちから隠そうとしているPythonの部分に勇敢に侵入しています。



さて、ここにPythonのこのような舞台裏があります。 私の手が届き、OllyDbgを介してこれらのオブジェクトを手動で変更しようとするとどうなるかを書くことができれば、と言います。



All Articles