Python socket程序内存泄漏的处理

Python socket程序内存泄漏的处理,python,sockets,memory-leaks,Python,Sockets,Memory Leaks,我在python中的套接字编程和线程方面没有太多经验。我正在研究一种跟踪设备,它可以将GPS位置数据发送到服务器。设备在长连接模式下工作,这意味着一旦设备连接到服务器,它将使连接保持活动(许多)小时 当我检查Ubuntu系统资源使用情况(使用top或htop)时,我发现我的进程的虚拟内存和驻留内存使用率都在增加。该进程从~55 MB虚拟内存和~12 MB驻留内存开始。3天后,虚拟内存增加到~14GB,而驻留内存使用量约为~90MB 这些统计数据在我看来并不正常,虚拟内存和驻留内存的使用率从未下降

我在python中的套接字编程和线程方面没有太多经验。我正在研究一种跟踪设备,它可以将GPS位置数据发送到服务器。设备在长连接模式下工作,这意味着一旦设备连接到服务器,它将使连接保持活动(许多)小时

当我检查Ubuntu系统资源使用情况(使用top或htop)时,我发现我的进程的虚拟内存和驻留内存使用率都在增加。该进程从~55 MB虚拟内存和~12 MB驻留内存开始。3天后,虚拟内存增加到~14GB,而驻留内存使用量约为~90MB

这些统计数据在我看来并不正常,虚拟内存和驻留内存的使用率从未下降,这让我怀疑内存泄漏。因为我对线程和套接字编程不是很有经验,所以我很难检查可能出现的问题

我的代码如下:

_log = Logger("Orion_EasyTrac").create_logger()

server = Server("", 9985, logger=_log)
server.start()
服务器

class Server(object):
    def __init__(self, host, sock_port, buffsize=8192, logger=None):
        self.hostname = host
        self.sock_port = sock_port
        self.buffsize = buffsize
        self.socket = None
        self.log = logger or _log

    def start(self):
        self.log.info("Listening: {prt}".format(prt=self.sock_port))
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind((self.hostname, self.sock_port))
        self.socket.listen(5)

        while True:
            conn, address = self.socket.accept()
            thread.start_new_thread(EasyTrackProHandler(conn=conn, buff_size=self.buffsize, log=self.log).handle_data, ())
EasyTrackPro的基本部分

class EasyTrackPro(object):
    def __init__(self, conn, buff_size, log):
        self.conn = conn
        self.buff_size = buff_size
        self.log = log
        self.db_conn = None                         
        self.cursor = None                          
        self.db_connector = None                    
        self.ack_parameter = None
        self.device_id = None
        self.user_id = None
        self.beaconID = None
        self.error_count = 0

    ...
    ...

    def handle_data(self):
        while True:
            try:
                _veri = self.conn.recv(self.buff_size)
                if not _veri:
                    # We do not recieve any data...
                    raise NoIncomingDataException()
            except NoIncomingDataException:
                # No need to log anything in here...
                break
            except Exception as h_e:
                self.log.exception(self._log_msg("Socket exception {h_e}".format(h_e=h_e)))
                break
            else:
                self.control_data(_veri)
        self.conn.close()
self.control\u data(\u veri)
是完成这项工作的一部分,但由于它相当长且复杂,因此共享该部分似乎不合逻辑

Logger
是一个使用python
logging
模块并将日志记录到文本文件的类

我的猜测是,垃圾收集器无法收集旧对象,或者由于设备使用长连接,服务器端从未关闭打开的套接字。但对其中任何一个都不确定

内存泄漏可能有什么问题

更新:我的记录器类

class Logger:
    def __init__(self, log_name, level='INFO', log_dir='logs', log_format=None, handler=None):
        """
        log_name:   log file name
        level:      log levels: CRITICAL, ERROR, WARNING, INFO, DEBUG
        log_dir:    log folder. default <file_path>/logs/
        log_format: logging format
        handler:    TODO (incomplete)
        """
        self.__log_name = log_name
        self.__formatter = None
        self.__handler = handler
        self.__level = None
        self.__log_format = u"%(asctime)s %(levelname)s %(name)s %(process)d %(threadName)s %(module)s: " \
                            u"%(lineno)d %(funcName)s(): %(message)s\r\n"
        self.loger = None

        self.__configure_level(level.upper())
        self.__configure_format(log_format)
        self.__configure_handler(log_dir)

    def __configure_level(self, level):
        if level and (level not in ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']):
            raise Exception(u"{lvl} is not a valid log level".format(lvl=level))
        self.__level = level or 'INFO'

    def __configure_format(self, log_format):
        if log_format:
            self.__log_format = log_format
        self.formatter = logging.Formatter(self.__log_format)

    def __configure_handler(self, log_dir):
        _dir = '{b_dr}/{l_dr}'.format(b_dr=os.path.dirname(os.path.abspath(__file__)), l_dr=log_dir)
        if not os.path.exists(_dir):
            os.mkdir(_dir)
        _filename = "{dr}/{lnf}.log".format(dr=_dir, lnf=self.__log_name)
        self.handler = handlers.WatchedFileHandler(_filename, mode="a", encoding="utf-8")
        self.handler.setFormatter(self.formatter)

    def create_logger(self):
        _loger = logging.getLogger(self.__log_name)
        _loger.setLevel(getattr(logging, self.__level))
        _loger.addHandler(self.handler)
        self.loger = _loger
        return _loger
类记录器:
def\uuu init\uuuu(self,log\u name,level='INFO',log\u dir='logs',log\u format=None,handler=None):
"""
日志名称:日志文件名
级别:日志级别:严重、错误、警告、信息、调试
日志目录:日志文件夹。默认值/logs/
日志格式:日志格式
处理程序:TODO(不完整)
"""
self.\uuuu log\u name=log\u name
self.\uu格式化程序=无
self.\uu handler=handler
自身水平=无
self.\uu log\u format=u“%(astime)s%(levelname)s%(name)s%(进程)d%(线程名称)s%(模块)s:”\
u“%(行号)d%(funcName)s():%(消息)s\r\n”
self.loger=None
self.\u配置\u级别(level.upper())
自我配置格式(日志格式)
self.\u配置\u处理程序(日志\u目录)
定义配置级别(自身,级别):
如果级别和(级别不在[‘严重’、‘错误’、‘警告’、‘信息’、‘调试’]):
引发异常(u“{lvl}不是有效的日志级别”。格式(lvl=level))
自我水平=水平或“信息”
定义配置格式(自我、日志格式):
如果日志格式为:
self.\uuuu log\u format=log\u format
self.formatter=logging.formatter(self.\u log\u格式)
def\uu配置\u处理程序(self,log\u dir):
_dir='{b_-dr}/{l_-dr}'。格式(b_-dr=os.path.dirname(os.path.abspath(u-file_u)),l_-dr=log_-dir)
如果操作系统路径不存在(\u dir):
os.mkdir(_dir)
_filename=“{dr}/{lnf}.log”。格式(dr=\u dir,lnf=self.\u log\u name)
self.handler=handlers.WatchedFileHandler(_filename,mode=“a”,encoding=“utf-8”)
self.handler.setFormatter(self.formatter)
def创建_记录器(自身):
_loger=logging.getLogger(self.\u log\u name)
_loger.setLevel(getattr(日志记录,自我级别))
_loger.addHandler(self.handler)
self.loger=\u loger
返回记录器

我使用日志记录查看为开发目的接收的数据。当您看到原始数据时,调试错误/bug会更容易。但我不认为日志记录是唯一的原因。但是日志记录保存到ram。您是否有任何一个清洁器功能(带周期)?带周期是什么意思?如何使用它?\u log=Logger(“Orion\u EasyTrac”)。在Logger类中创建\u Logger()!我不知道你的“记录者”是谁。伸出手指,想要做全面检查(我不是算命师)?但对于起点(如果记录器不是外部的),请尝试:help(记录器)或print dir(记录器)。希望对您有所帮助。致以最良好的祝愿@你解决问题了吗?我有一个非常相似的。我正在使用多线程的侦听和客户端方法来创建套接字连接。我的程序单独运行时使用大约600 MB的内存,但如果我使用socket将其放入一个循环中以更改配置文件,然后运行它,则每次运行时都会增加内存,直到运行30次左右后崩溃。我试图删除在循环结束时创建的对象,但不起作用。所以我认为这是socket的一个问题。我使用日志记录来查看为开发目的接收的数据。当您看到原始数据时,调试错误/bug会更容易。但我不认为日志记录是唯一的原因。但是日志记录保存到ram。您是否有任何一个清洁器功能(带周期)?带周期是什么意思?如何使用它?\u log=Logger(“Orion\u EasyTrac”)。在Logger类中创建\u Logger()!我不知道你的“记录者”是谁。伸出手指,想要做全面检查(我不是算命师)?但对于起点(如果记录器不是外部的),请尝试:help(记录器)或print dir(记录器)。希望对您有所帮助。致以最良好的祝愿@你解决问题了吗?我有一个非常相似的。我正在使用多线程的侦听和客户端方法来创建套接字连接。我的程序单独运行时使用大约600 MB的内存,但如果我使用socket将其放入一个循环中以更改配置文件,然后运行它,则每次运行时都会增加内存,直到运行30次左右后崩溃。我试图删除在循环结束时创建的对象,但不起作用。所以我认为这是插座的问题。