Ich habe eine PHP-Erweiterung mit SWIG erstellt und alles funktioniert gut, aber ich beobachte einige seltsame Garbage Collection Verhalten beim Verketten Methodenaufrufe. Zum Beispiel funktioniert dies:Ressourcenmüll zu früh gesammelt
$results = $response->results();
$row = $results->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
Aber diese seg Fehler:
$row = $response->results()->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
Der einzige Unterschied ist, dass die erste $results
erzeugt, während die zweiten Ketten die Anrufe zusammen.
SWIG stellt PHP nur Funktionen zur Verfügung und generiert PHP-Proxy-Klassen, um mit ihnen zu interagieren. Diese Proxy-Klassen enthalten im Wesentlichen eine Ressource, die an jede der exponierten Funktionen gemeinsam mit allen anderen Argumenten übergeben wird, die diese Funktionen normalerweise annehmen würden. Da ich dachte, dass diese Proxy-Klassen vielleicht das Problem waren, habe ich den Code überarbeitet, um sie zu umgehen und stattdessen die exponierten Funktionen direkt zu verwenden. Nach wie vor das funktioniert:
$results = InvocationResponse_results($response->_cPtr);
$row = TableIterator_next(Table_iterator(Tables_get($results, 0)));
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1));
Und wieder diese seg Fehler:
$row = TableIterator_next(Table_iterator(Tables_get(InvocationResponse_results($response->_cPtr), 0)));
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1));
Auch hier ist der einzige Unterschied, dass die erste $results
erzeugt, während die zweiten Ketten zusammen die Anrufe.
An diesem Punkt verbrachte ich eine Weile Debugging in gdb/valgrind und festgestellt, dass der Destruktor für was InvocationResponse_results
zurückgibt wird zu früh beim Verketten von Anrufen zusammen aufgerufen. Um zu beobachten, habe ich std::cout
Anweisungen an den Spitzen der exponierten C++ - Funktionen und deren Destruktoren eingefügt. Dies ist die Ausgabe ohne Verkettungs:
InvocationResponse_results()
Tables_get()
Table_iterator()
TableIterator_next()
__wrap_delete_TableIterator
Row_getString()
Row_getString()
Hola Mundo
---
__wrap_delete_InvocationResponse
__wrap_delete_Row
__wrap_delete_Tables
I ---
am Ende des Skripts unterscheiden zu können gedruckt, was während des Skripts Ausführung geschieht und was passiert danach. Hola Mundo
ist von printf
. Der Rest stammt aus C++. Wie Sie sehen können, wird alles in der erwarteten Reihenfolge aufgerufen. Destruktoren werden nur nach der Ausführung des Skripts aufgerufen, obwohl der Destruktor TableIterator
früher aufgerufen wird, als ich erwartet hätte. Dies hat jedoch keine Probleme verursacht und ist wahrscheinlich nicht damit verbunden. Nun vergleichen Sie diese mit dem Ausgang mit Chaining:
InvocationResponse_results()
Tables_get()
__wrap_delete_Tables
Table_iterator()
TableIterator_next()
__wrap_delete_TableIterator
Row_getString()
Segmentation fault (core dumped)
Ohne den Rückgabewert von InvocationResponse_results
in $results
gespeichert wird, ist es glücklich Müll vor der Ausführung selbst gesammelte steigt aus der Call-Kette (zwischen Tables_get
und Table_iterator
) und diese schnell verursacht Probleme auf der Straße, was letztlich zu einem Seg-Fehler führt.
Ich habe auch Referenzzählungen unter Verwendung xdebug_debug_zval()
in verschiedenen Orten inspiziert, aber nichts ungewöhnliches entdeckt. Hier ist die Ausgabe auf $results
und $row
ohne Verkettungs:
results: (refcount=1, is_ref=0)=resource(18) of type (_p_std__vectorT_voltdb__Table_t)
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row)
Und auf $row
mit Chaining:
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row)
ich ein paar Tage auf das jetzt damit verbracht habe, und ich bin gerade dabei, die Ideen , so würde wirklich jeder Einblick, wie man das löst, sehr geschätzt werden.
Es ist sehr unwahrscheinlich, dass jemand ohne psychische Debugging-Kräfte in der Lage sein wird, dies herauszufinden. Ich schlage vor, Sie setzen einen Haltepunkt in '_zend_list_delete' und finden heraus, warum der aufrufende Code die Ressource löscht. Es kann der Ressourcenrefcount sein, der 0 trifft oder direkt gelöscht wird. – Artefacto
@Artefacto Ich schaute in '_zend_list_delete', während '__wrap_delete_Tables' aufgerufen wurde und in beiden Fällen (kein seg Fehler und seg Fehler), ist es Müll gesammelt, weil sein refcount (' --le-> refcount') -1 ist. –
So finden Sie heraus, warum '__wrap_delete_Tables' zu dieser bestimmten Zeit in einer Gelegenheit, aber nicht in der anderen aufgerufen wird und weiter geht. – Artefacto