2010-08-19 56 views
9

我已經創建了一個PHP擴展SWIG和一切工作正常,但我觀察到鏈方法調用時一些奇怪的垃圾收集行爲。例如,這個工程:資源垃圾太早收集

$results = $response->results(); 
$row = $results->get(0)->iterator()->next(); 
printf('%s %s' . "\n", $row->getString(0), $row->getString(1)); 

但這賽格故障:

$row = $response->results()->get(0)->iterator()->next(); 
printf('%s %s' . "\n", $row->getString(0), $row->getString(1)); 

唯一的區別是,先創建$results,而第二鏈的通話在一起。

SWIG實際上只向PHP公開功能,並生成PHP代理類來與它們進行交互。這些代理類基本上擁有傳遞給每個暴露函數的資源以及這些函數通常會採用的任何其他參數。考慮到也許這些代理類是問題,我重新編寫代碼繞過它們,直接使用暴露的函數。和以前一樣,這個作品:

$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)); 

再次,這賽格故障:

$row = TableIterator_next(Table_iterator(Tables_get(InvocationResponse_results($response->_cPtr), 0))); 
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1)); 

再次,唯一的區別是,第一個創建$results,而第二鏈的通話在一起。

此時,我花了一段時間在gdb/valgrind中進行調試,並確定在將調用鏈接在一起時,調用InvocationResponse_results返回的析構函數太早。爲了觀察,我在暴露的C++函數及其析構函數的頂部插入了std::cout語句。這是輸出無鏈:

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 

我打印---在腳本結束時能夠區分腳本的執行過程中發生了什麼,並會發生什麼了。 Hola Mundo來自printf。其餘來自C++。正如你所看到的,一切都按照預期的順序被調用。析構函數只在腳本執行後被調用,儘管析構函數的調用時間比我預期的要早。但是,這並沒有造成任何問題,可能與此無關。現在比較這對輸出與鏈接:

InvocationResponse_results() 
Tables_get() 
__wrap_delete_Tables 
Table_iterator() 
TableIterator_next() 
__wrap_delete_TableIterator 
Row_getString() 
Segmentation fault (core dumped) 

如果沒有InvocationResponse_results的返回值被保存到$results,很開心地在執行之前,甚至失控的調用鏈(間Tables_getTable_iterator),這迅速收集垃圾導致道路上出現問題,最終導致seg故障。

我還在不同地方使用xdebug_debug_zval()檢查了參考計數,但沒有發現任何異常。下面是其對$results$row輸出,無需鏈接:

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) 

而且在$row與鏈接:

row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row) 

我花了幾天時間在這現在,我只是出出主意,所以真的有關如何解決這個問題的任何洞察力將不勝感激。

+0

這是非常不可能的,沒有心理調試權力的人將能夠弄清楚這一點。我建議你在'_zend_list_delete'中放置一個斷點,並找出調用代碼爲什麼要刪除資源。它可能是資源refcount達到0或直接刪除。 – Artefacto 2010-08-19 22:34:04

+0

@Artefacto在調用__wrap_delete_Tables時,我在_zend_list_delete裏面窺視,並且在兩種情況下(沒有seg fault和seg fault),它都是垃圾收集的,因爲它的refcount('--le-> refcount')是-1。 – 2010-08-20 15:11:31

+0

因此,找出爲什麼'__wrap_delete_Tables'在某個特定時間被調用,而不是另一個,並且繼續上升。 – Artefacto 2010-08-20 15:32:20

回答

1

This原來是類似的調試問題segfaulting問題的一部分。 (什麼Artefacto說)