3

問題は解決されませんでしたが、次の存在が原因でした:

def __del__(self):
    print "deleted!"

モデルクラスで。モデルクラスから削除するとすぐに、メモリ使用量の問題を実験できなくなりました。

アドホック セッション/エンジンで使用しているいくつかのモデルがあり、これは正常に動作していますが、データベース/セッション コンテキストの外部でこれらのオブジェクトを作成したい場合、SQLAlchemy インストルメンテーションがオブジェクトの参照を保持しているようです。削除されることはありません。

私がやりたいのは、「通常の」Pythonオブジェクトのようなモデルオブジェクトを作成し、それをセッション/データベースに追加しないことです(ただし、他のコンテキストではデータベースに追加する必要があります)。これは間違っていますか?

import gc
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String

base = declarative_base()

class SomeObject(base):
    __tablename__ = "SomeObject"

    instrumented_name = Column('name', String(55), primary_key=True)
    uninstrumented_name = None

    def __del__(self):
        print "deleted!"

obj1 = SomeObject()
obj1.uninstrumented_name = "foo"
obj1 = None

#obj1 is properly deleted

obj2 = SomeObject()
obj2.instrumented_name = "bar"
obj2 = None
gc.collect()

#obj2 never deleted

編集私はいくつかの追加のテストを行いましたが、オブジェクトがセッションにコミットされない場合 (ロールバックなど)、SQLAlchemy がメモリ リークを引き起こすようです。

インストルメント化されたオブジェクトの参照を SQLAlchemy に強制的に解放させる方法はありますか?

import gc
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, create_engine

Base = declarative_base()

class MemoryMonster(Base):
    __tablename__ = "MemoryMonster"

    _id = Column('id', Integer(), primary_key=True)
    _name = Column('name', String(55))

    def __init__(self):
        self._name = "some monster name"
        self._eat_some_ram = ' ' * 1048576

    def __del__(self):
        print "deleted!"


engine = create_engine("sqlite:///:memory:")
session_factory = sessionmaker(engine)
Session = scoped_session(session_factory)

Base.metadata.create_all(engine)


def create_and_commit():
    session = Session()
    for _ in range(100):
        session.add(MemoryMonster())
    session.commit()
    Session.remove()
    gc.collect()


def create_and_rollback():
    session = Session()
    for _ in range(100):
        monster = MemoryMonster()
        session.add(monster)
        session.expunge(monster)
    session.rollback()
    Session.remove()
    gc.collect()


def create_do_not_include_in_session():
    session = Session()
    for _ in range(100):
        monster = MemoryMonster()
    session.rollback()
    Session.remove()
    gc.collect()


# Scenario 1 - Objects included in the session and commited
# No memory leak
create_and_commit()

# Scenario 2 - Objects included in the session and rollbacked
# Memory leak
create_and_rollback()

# Scenario 3 - Objects are not not included in the session
# Memory leak
create_do_not_include_in_session()
4

1 に答える 1

4

を使用すると、メモリ リーク__del__()が発生する可能性があります。これは、オブジェクトが循環の対象になると、循環 GC によって到達できなくなるためです。これがこのテストのケースです。これは、オブジェクトが「ダーティ」である場合、つまり、データベースにフラッシュされる保留中の属性がある場合に、SQLAlchemy オブジェクト インストルメンテーションがオブジェクトからそれ自体へのサイクルを作成するためです。ダーティ オブジェクトをセッションに追加すると、それへのすべての参照が失われますが、変更は引き続きフラッシュされます。この「参照マーカー」は、オブジェクトがクリーンとしてマークされる (つまり、フラッシュされる) とすぐに削除されます。

SQLAlchemy 0.8.1 では、この動作を改善しました。1 つは、この参照サイクルが「保留中」または「切り離された」オブジェクト、つまりセッションに関連付けられていないオブジェクトに対して作成されなくなったことです。代わりに、オブジェクトがセッションにアタッチされたときに .modified フラグがチェックされ、参照マーカーはその時点でのみ関連付けられます (そして、オブジェクトがクリーンになると削除されます)。オブジェクトがセッションから切り離された場合、オブジェクトがまだ変更されていても、マーカーは無条件に削除されます。.modified フラグは true のままです。

さらに、クラスが最初にマップされ、メソッドがあると検出されたときに警告を追加しました__del__()。Python オブジェクトが循環を持つことは非常に簡単で、SQLAlchemy の場合relationship()、backref を使用してクラスに を配置するパターンも参照循環を作成する効果があるため、ここでの状態管理の改善があっても、使用__del__()は悪いアイデア。

于 2013-04-20T06:54:25.123 に答える