Questo metodo itera su un elenco di termini nel database, controlla se i termini sono in un testo passato come argomento e, se lo si è, sostituirlo con un collegamento alla pagina di ricerca con il termine come parametro.Perché questo metodo Python perde memoria?
Il numero di termini è elevato (circa 100000), quindi il processo è piuttosto lento, ma questo è Ok poiché viene eseguito come cron job. Tuttavia, fa sì che la memoria consumtion sceneggiatura a razzo e non riesco a trovare il motivo per cui:
class SearchedTerm(models.Model):
[...]
@classmethod
def add_search_links_to_text(cls, string, count=3, queryset=None):
"""
Take a list of all researched terms and search them in the
text. If they exist, turn them into links to the search
page.
This process is limited to `count` replacements maximum.
WARNING: because the sites got different URLS schemas, we don't
provides direct links, but we inject the {% url %} tag
so it must be rendered before display. You can use the `eval`
tag from `libs` for this. Since they got different namespace as
well, we enter a generic 'namespace' and delegate to the
template to change it with the proper one as well.
If you have a batch process to do, you can pass a query set
that will be used instead of getting all searched term at
each calls.
"""
found = 0
terms = queryset or cls.on_site.all()
# to avoid duplicate searched terms to be replaced twice
# keep a list of already linkified content
# added words we are going to insert with the link so they won't match
# in case of multi passes
processed = set((u'video', u'streaming', u'title',
u'search', u'namespace', u'href', u'title',
u'url'))
for term in terms:
text = term.text.lower()
# no small word and make
# quick check to avoid all the rest of the matching
if len(text) < 3 or text not in string:
continue
if found and cls._is_processed(text, processed):
continue
# match the search word with accent, for any case
# ensure this is not part of a word by including
# two 'non-letter' character on both ends of the word
pattern = re.compile(ur'([^\w]|^)(%s)([^\w]|$)' % text,
re.UNICODE|re.IGNORECASE)
if re.search(pattern, string):
found += 1
# create the link string
# replace the word in the description
# use back references (\1, \2, etc) to preserve the original
# formatin
# use raw unicode strings (ur"string" notation) to avoid
# problems with accents and escaping
query = '-'.join(term.text.split())
url = ur'{%% url namespace:static-search "%s" %%}' % query
replace_with = ur'\1<a title="\2 video streaming" href="%s">\2</a>\3' % url
string = re.sub(pattern, replace_with, string)
processed.add(text)
if found >= 3:
break
return string
Probabilmente si vorrà questo codice così:
class SearchedTerm(models.Model):
[...]
@classmethod
def _is_processed(cls, text, processed):
"""
Check if the text if part of the already processed string
we don't use `in` the set, but `in ` each strings of the set
to avoid subtring matching that will destroy the tags.
This is mainly an utility function so you probably won't use
it directly.
"""
if text in processed:
return True
return any(((text in string) for string in processed))
ho davvero solo due oggetti con riferimenti che potrebbero essere i sospetti qui: terms
e processed
. Ma non riesco a vedere alcuna ragione per loro di non essere spazzatura raccolta.
EDIT:
penso che dovrei dire che questo metodo viene chiamato all'interno di un metodo modello di Django stessa. Non so se è rilevante, ma qui è il codice:
class Video(models.Model):
[...]
def update_html_description(self, links=3, queryset=None):
"""
Take a list of all researched terms and search them in the
description. If they exist, turn them into links to the search
engine. Put the reset into `html_description`.
This use `add_search_link_to_text` and has therefor, the same
limitations.
It DOESN'T call save().
"""
queryset = queryset or SearchedTerm.objects.filter(sites__in=self.sites.all())
text = self.description or self.title
self.html_description = SearchedTerm.add_search_links_to_text(text,
links,
queryset)
Posso immaginare che il caching automatico Python regex mangia un po 'di memoria. Ma dovrebbe farlo solo una volta e il consumo di memoria sale ad ogni chiamata di update_html_description
.
Il problema non è solo che consuma un sacco di memoria, il problema è che non rilasciarlo: ogni telefonata prendono circa il 3% del montone, eventualmente riempiendolo e schiantarsi lo script con 'non può allocare memoria '.
'' È quasi impossibile liberare memoria in un linguaggio spazzato come Python. A rigor di termini, una perdita di memoria è una memoria che non ha riferimenti variabili ad essa. In C++ se si assegna memoria in una classe, ma non si dichiara un distruttore, si può avere una perdita di memoria. Quello che hai qui è semplicemente un elevato consumo di memoria. ' –
:-) Ok. Poi ho ottenuto un elevato consumo di memoria che diventa sempre più alto dopo ogni chiamata. Ma dal momento che è un metodo. E dal momento che non tengo un riferimento a nulla dopo averlo fatto, perché qualcosa consuma ancora memoria? –
Aggiornato la domanda a riguardo. –