Description
Crash report
What happened?
This is a segfault I am seeing on Python 3.12, when trying to use the Python and Julia runtimes simultaneously via the PyJulia package.
It seems like when there is an object that is referenced by both the Julia and Python runtimes, there can be memory access errors. It seems as though Python is trying to free memory which has already been freed in Julia or vice versa.
I am raising the issue here since the issue has only started occurring on Python 3.12, but does not occur on 3.11. The Julia version does not seem to affect this behavior. So I am trying to understand what changes were made to the Python GC that might have triggered this, and if perhaps the GC is more aggressive in some way?
Here is my current MWE based on a package I maintain (PySR) that uses Julia as backend for a Python frontend:
from pysr import PySRRegressor
model = PySRRegressor()
# This runs fine:
model.fit([[1]], [1])
# This second run results in a segmentation fault:
model.fit([[1]], [1])
This is the smallest MWE I have been able to create thus far.
I also see the issue in my continuous integration tests on Python 3.12, but never before 3.12: MilesCranmer/PySR#450
For example, in one of those segfaults, I see the following backtrace:
Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.
Exception: EXCEPTION_ACCESS_VIOLATION at 0x7ffb0691a026 -- PyObject_Free at C:\hostedtoolcache\windows\Python\3.12.1\x64\python312.dll (unknown line)
in expression starting at none:0
PyObject_Free at C:\hostedtoolcache\windows\Python\3.12.1\x64\python312.dll (unknown line)
pydecref_ at C:\Users\runneradmin\.julia\packages\PyCall\1gn3u\src\PyCall.jl:118
pydecref at C:\Users\runneradmin\.julia\packages\PyCall\1gn3u\src\PyCall.jl:123
jfptr_pydecref_1039 at C:\Users\runneradmin\.julia\compiled\v1.9\PyCall\GkzkC_As42O.dll (unknown line)
run_finalizer at C:/workdir/src\gc.c:417
jl_gc_run_finalizers_in_list at C:/workdir/src\gc.c:507
run_finalizers at C:/workdir/src\gc.c:553
run_finalizers at C:/workdir/src\gc.c:534 [inlined]
ijl_gc_collect at C:/workdir/src\gc.c:3732
maybe_collect at C:/workdir/src\gc.c:1083 [inlined]
jl_gc_pool_alloc_inner at C:/workdir/src\gc.c:1450 [inlined]
jl_gc_pool_alloc_noinline at C:/workdir/src\gc.c:1511
jl_gc_alloc_ at C:/workdir/src\julia_internal.h:460 [inlined]
_new_array_ at C:/workdir/src\array.c:144
_new_array at C:/workdir/src\array.c:198 [inlined]
ijl_alloc_array_1d at C:/workdir/src\array.c:436
Array at .\boot.jl:477 [inlined]
Array at .\boot.jl:486 [inlined]
similar at .\array.jl:374 [inlined]
similar at .\abstractarray.jl:839 [inlined]
deg2_l0_r0_eval at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:257
jfptr_deg2_l0_r0_eval_1577 at C:\Users\runneradmin\.julia\compiled\v1.9\DynamicExpressions\BQC8W_As42O.dll (unknown line)
_eval_tree_array at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:117
_eval_tree_array at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:131
_eval_tree_array at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:125
#eval_tree_array#1 at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:65 [inlined]
eval_tree_array at C:\Users\runneradmin\.julia\packages\DynamicExpressions\KRT17\src\EvaluateEquation.jl:59 [inlined]
#eval_tree_array#1 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\InterfaceDynamicExpressions.jl:57 [inlined]
eval_tree_array at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\InterfaceDynamicExpressions.jl:56 [inlined]
_eval_loss at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\LossFunctions.jl:48
#eval_loss#3 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\LossFunctions.jl:101
eval_loss at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\LossFunctions.jl:93 [inlined]
#score_func#5 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\LossFunctions.jl:160 [inlined]
score_func at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\LossFunctions.jl:157 [inlined]
#next_generation#1 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\Mutate.jl:235
next_generation at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\Mutate.jl:60 [inlined]
reg_evol_cycle at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\RegularizedEvolution.jl:37
#s_r_cycle#1 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\SingleIteration.jl:42
s_r_cycle at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\SingleIteration.jl:17 [inlined]
#_dispatch_s_r_cycle#81 at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\SymbolicRegression.jl:1053
_dispatch_s_r_cycle at C:\Users\runneradmin\.julia\packages\SymbolicRegression\OYvt5\src\SymbolicRegression.jl:1036
I found this quite odd as it seems as though both the Julia and Python garbage collection are interfering with eachother. Here, it seems as though PyObject_Free
is trying to free memory that was already freed? Perhaps one of the GCs is trying to free the memory access by the other runtime. Looking at the backtrace, I suppose this could also be an issue with PyCall.jl (which calls Python functions from Julia), although it hasn't occurred in any previous Python version, so I'm not sure where the issue is coming from.
Any help is appreciated. I am happy to provide you with as much debugging information as I can, as this issue is quite urgent to fix in the ecosystem of Python <-> Julia packages.
CPython versions tested on:
3.12
Operating systems tested on:
Linux, macOS, Windows
Output from running 'python -VV' on the command line:
Linux test performed on: Python 3.12.1 (main, Dec 30 2023, 22:23:57) [GCC 8.5.0 20210514 (Red Hat 8.5.0-20)]