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,我改进了这种行为:一个是不再为“挂起”或“分离”对象创建此引用循环,即与会话无关的对象。相反,当对象附加到 .modified 标志的 Session 时,会检查该对象,并且仅在该点关联引用标记(并在对象变得干净时被删除,就像已经发生的情况一样)。如果对象从会话中分离,则标记将被无条件删除,即使对象仍有更改 - .modified 标志保持为真。

__del__()此外,当一个类第一次被映射并被检测为具有方法时,我添加了一个警告。Python 对象很容易有循环,在 SQLAlchemy 的情况下,将 arelationship()放在带有 backref 的类上的模式也会产生创建引用循环的效果,所以即使这里的状态管理改进,使用__del__()也是馊主意。

于 2013-04-20T06:54:25.123 回答