Fix memory leak caused by list append ref count, lack of setting NPY_ARRAY_OWNDATA

Change-Id: I22b284add9268c48278e56e54f1b8be0ab6a2593
This commit is contained in:
Wes McKinney committed 2017-05-14 11:52:53 -04:00
1 parent 5739e04b35
commit 84591238ca
3 files changed
+53 -14

No files matched your search

+6 -1
View File
@@ -69,7 +69,7 @@ class ARROW_EXPORT OwnedRef {
~OwnedRef() {
PyAcquireGIL lock;
Py_XDECREF(obj_);
release();
}
void reset(PyObject* obj) {
@@ -80,6 +80,11 @@ class ARROW_EXPORT OwnedRef {
obj_ = obj;
}
void release() {
Py_XDECREF(obj_);
obj_ = nullptr;
}
PyObject* obj() const { return obj_; }
private:
+12 -13
View File
@@ -1023,7 +1023,8 @@ static inline PyObject* NewArray1DFromType(
}
set_numpy_metadata(type, arrow_type, descr);
return PyArray_NewFromDescr(&PyArray_Type, descr, 1, dims, nullptr, data, 0, nullptr);
return PyArray_NewFromDescr(&PyArray_Type, descr, 1, dims, nullptr, data,
NPY_ARRAY_OWNDATA | NPY_ARRAY_CARRAY, nullptr);
}
class PandasBlock {
@@ -1078,12 +1079,10 @@ class PandasBlock {
PyObject* block_arr;
if (ndim == 2) {
npy_intp block_dims[2] = {num_columns_, num_rows_};
block_arr = PyArray_NewFromDescr(
&PyArray_Type, descr, 2, block_dims, nullptr, nullptr, 0, nullptr);
block_arr = PyArray_SimpleNewFromDescr(2, block_dims, descr);
} else {
npy_intp block_dims[1] = {num_rows_};
block_arr = PyArray_NewFromDescr(
&PyArray_Type, descr, 1, block_dims, nullptr, nullptr, 0, nullptr);
block_arr = PyArray_SimpleNewFromDescr(1, block_dims, descr);
}
if (block_arr == NULL) {
@@ -1091,6 +1090,8 @@ class PandasBlock {
return Status::OK();
}
PyArray_ENABLEFLAGS(reinterpret_cast<PyArrayObject*>(block_arr), NPY_ARRAY_OWNDATA);
npy_intp placement_dims[1] = {num_columns_};
PyObject* placement_arr = PyArray_SimpleNew(1, placement_dims, NPY_INT64);
if (placement_arr == NULL) {
@@ -1357,8 +1358,6 @@ inline void ConvertDatetimeNanos(const ChunkedArray& data, int64_t* out_values)
class ObjectBlock : public PandasBlock {
public:
using PandasBlock::PandasBlock;
virtual ~ObjectBlock() {}
Status Allocate() override { return AllocateNDArray(NPY_OBJECT); }
Status Write(const std::shared_ptr<Column>& col, int64_t abs_placement,
@@ -1416,7 +1415,6 @@ template <int ARROW_TYPE, typename C_TYPE>
class IntBlock : public PandasBlock {
public:
using PandasBlock::PandasBlock;
Status Allocate() override {
return AllocateNDArray(arrow_traits<ARROW_TYPE>::npy_type);
}
@@ -1450,7 +1448,6 @@ using Int64Block = IntBlock<Type::INT64, int64_t>;
class Float32Block : public PandasBlock {
public:
using PandasBlock::PandasBlock;
Status Allocate() override { return AllocateNDArray(NPY_FLOAT32); }
Status Write(const std::shared_ptr<Column>& col, int64_t abs_placement,
@@ -1470,7 +1467,6 @@ class Float32Block : public PandasBlock {
class Float64Block : public PandasBlock {
public:
using PandasBlock::PandasBlock;
Status Allocate() override { return AllocateNDArray(NPY_FLOAT64); }
Status Write(const std::shared_ptr<Column>& col, int64_t abs_placement,
@@ -1523,7 +1519,6 @@ class Float64Block : public PandasBlock {
class BoolBlock : public PandasBlock {
public:
using PandasBlock::PandasBlock;
Status Allocate() override { return AllocateNDArray(NPY_BOOL); }
Status Write(const std::shared_ptr<Column>& col, int64_t abs_placement,
@@ -1544,7 +1539,6 @@ class BoolBlock : public PandasBlock {
class DatetimeBlock : public PandasBlock {
public:
using PandasBlock::PandasBlock;
Status AllocateDatetime(int ndim) {
RETURN_NOT_OK(AllocateNDArray(NPY_DATETIME, ndim));
@@ -1629,7 +1623,6 @@ template <int ARROW_INDEX_TYPE>
class CategoricalBlock : public PandasBlock {
public:
explicit CategoricalBlock(int64_t num_rows) : PandasBlock(num_rows, 1) {}
Status Allocate() override {
constexpr int npy_type = arrow_traits<ARROW_INDEX_TYPE>::npy_type;
@@ -1960,6 +1953,9 @@ class DataFrameBlockCreator {
PyObject* item;
RETURN_NOT_OK(it.second->GetPyResult(&item));
if (PyList_Append(list, item) < 0) { RETURN_IF_PYERROR(); }
// ARROW-1017; PyList_Append increments object refcount
Py_DECREF(item);
}
return Status::OK();
}
@@ -2045,6 +2041,9 @@ class ArrowDeserializer {
// Arrow data is immutable.
PyArray_CLEARFLAGS(arr_, NPY_ARRAY_WRITEABLE);
// Arrow data is owned by another
PyArray_CLEARFLAGS(arr_, NPY_ARRAY_OWNDATA);
return Status::OK();
}
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import pyarrow as pa
import numpy as np
import memory_profiler
import gc
def leak():
data = [pa.array(np.concatenate([np.random.randn(100000)] * 1000))]
table = pa.Table.from_arrays(data, ['foo'])
while True:
print('calling to_pandas')
print('memory_usage: {0}'.format(memory_profiler.memory_usage()))
table.to_pandas()
gc.collect()
leak()