检测对象未提交时SQLAlchemy内存泄漏(例如回滚)

检测对象未提交时SQLAlchemy内存泄漏(例如回滚),sqlalchemy,Sqlalchemy,已解决不是一个问题,是由以下情况引起的: def __del__(self): print "deleted!" 在模型课上。一旦我将其从模型类中删除,我就无法尝试内存使用方面的任何问题 我有一些模型,我正在使用一个特别的会话/引擎,这很好,但是当我想在数据库/会话上下文之外创建这些对象时,SQLAlchemy instrumentation似乎保留了对对象的引用,并且它们永远不会被删除 我想做的是创建一个类似于“普通”Python对象的模型对象,并且从不将其添加到任何会话/数据库(但

已解决不是一个问题,是由以下情况引起的:

def __del__(self):
    print "deleted!"
在模型课上。一旦我将其从模型类中删除,我就无法尝试内存使用方面的任何问题

我有一些模型,我正在使用一个特别的会话/引擎,这很好,但是当我想在数据库/会话上下文之外创建这些对象时,SQLAlchemy instrumentation似乎保留了对对象的引用,并且它们永远不会被删除

我想做的是创建一个类似于“普通”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()
使用
\uuu del\uuu()
可以,因为如果对象成为循环的主题,那么循环GC就无法访问它。在本测试中就是这种情况,因为SQLAlchemy对象检测在对象“脏”的情况下创建了一个从对象到自身的循环,也就是说,具有要刷新到数据库的挂起属性,因此您可以将脏对象添加到会话中,然后丢失对它的所有引用,并且更改仍将被刷新。一旦物体被标记为清洁(即冲洗),该“参考标记”即被移除

对于SQLAlchemy 0.8.1,我已经说过:一个是不再为“挂起”或“分离”对象(即与会话无关的对象)创建此引用周期。相反,当对象附加到.modified标志的会话时,将检查该对象,并且引用标记仅在该点关联(并且在对象变为干净时移除,就像已经发生的情况一样)。如果对象与会话分离,则标记将无条件删除,即使对象仍有更改,-.modified标志仍为true


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

您如何定义“内存泄漏”? 我没有看到您在这里计算gc.get_objects(),这是查看对象未被释放的唯一方法。Python解释器本身通常不会在获取内存后释放内存。在释放对对象的最后一次引用时应该调用“del”方法,不是吗?我看到调用del的唯一情况是当我提交对象时。实际上,使用
\uu del\uuu
是一种很好的方法,因为如果对象成为循环的主体,那么循环GC就无法访问它。因此,我假设在rollback()执行其工作的过程中,正在创建一个循环,并且由于
\uu del\uu
对象变得不可访问。使用len(gc.get_objects())代替。另外,如果我循环“create_and_commit”,Python解释器的内存使用是完全没有变化的。如果我循环“create_and_rollback”,内存增长非常快。所以我很好奇为什么我们在调用rollback()时会得到一个循环,看看这个循环。我们尽量不在用户对象周围创建循环。