Repository: numba/numba

Issue:
Memory leak when returning array from inner function?
If you try the following (here under 64-bit Linux):

```
>>> import numpy as np
>>> from numba import jit
>>> def f(x): return np.ones(x)
>>> ff = jit(nopython=True)(f)
>>> %timeit f(1000000)
1000 loops, best of 3: 875 µs per loop
>>> %timeit ff(1000000)
The slowest run took 44.83 times longer than the fastest. This could mean that an intermediate result is being cached 
100 loops, best of 3: 3.09 ms per loop
```

you may find that the `%timeit ff(...)` line leaks a lot of memory (up to several gigabytes), while the `%timeit f(...)` line doesn't. Furthermore, if you replace `np.ones` with `np.zeros`, the issue disappears.
This leads me to postulate that there is a leak when an array is returned from an inner function (which is how np.ones() is implemented).



Discussion:
Also leak with no return

```
def f(x):
    np.ones(x)
```

Ok, this is not happening to `np.zeros`.  


Resolving patch:
diff --git a/numba/lowering.py b/numba/lowering.py
--- a/numba/lowering.py
+++ b/numba/lowering.py
@@ -198,8 +198,12 @@ def lower_inst(self, inst):
                 if isinstance(inst.value, ir.Expr) and inst.value.op == 'call':
                     callexpr = inst.value
                     # NPM function returns new reference
-                    if isinstance(self.typeof(callexpr.func.name),
-                                  types.Dispatcher):
+                    fnty = self.typeof(callexpr.func.name)
+                    if (isinstance(fnty, types.Dispatcher)
+                        or (isinstance(fnty, types.Function)
+                            and getattr(fnty.template,
+                                        'return_new_reference',
+                                        False))):
                         self.decref(ty, val)
 
         elif isinstance(inst, ir.Branch):
diff --git a/numba/targets/arrayobj.py b/numba/targets/arrayobj.py
--- a/numba/targets/arrayobj.py
+++ b/numba/targets/arrayobj.py
@@ -1325,6 +1325,7 @@ def _empty_nd_impl(context, builder, arrtype, shapes):
                    strides=cgutils.pack_array(builder, strides),
                    itemsize=itemsize,
                    meminfo=meminfo)
+
     return ary
 
 def _zero_fill_array(context, builder, ary):
@@ -1382,7 +1383,7 @@ def numpy_empty_nd(context, builder, sig, args):
 @builtin
 @implement(numpy.empty_like, types.Kind(types.Array))
 @implement(numpy.empty_like, types.Kind(types.Array), types.Kind(types.DTypeSpec))
-def numpy_zeros_like_nd(context, builder, sig, args):
+def numpy_empty_like_nd(context, builder, sig, args):
     arrtype, shapes = _parse_empty_like_args(context, builder, sig, args)
     ary = _empty_nd_impl(context, builder, arrtype, shapes)
     return ary._getvalue()
diff --git a/numba/typing/npydecl.py b/numba/typing/npydecl.py
--- a/numba/typing/npydecl.py
+++ b/numba/typing/npydecl.py
@@ -433,6 +433,7 @@ class NdZeros(NdConstructor):
 @builtin
 class NdOnes(NdConstructor):
     key = numpy.ones
+    return_new_reference = True
 
 @builtin
 class NdEmptyLike(NdConstructorLike):
@@ -462,6 +463,8 @@ class NdOnesLike(NdConstructorLike):
     @builtin
     class NdFull(CallableTemplate):
         key = numpy.full
+        return_new_reference = True
+
 
         def generic(self):
             def typer(shape, fill_value, dtype=None):
@@ -479,6 +482,7 @@ def typer(shape, fill_value, dtype=None):
     @builtin
     class NdFullLike(CallableTemplate):
         key = numpy.full_like
+        return_new_reference = True
 
         def generic(self):
             def typer(arr, fill_value, dtype=None):
@@ -498,6 +502,7 @@ def typer(arr, fill_value, dtype=None):
 @builtin
 class NdIdentity(AbstractTemplate):
     key = numpy.identity
+    return_new_reference = True
 
     def generic(self, args, kws):
         assert not kws
@@ -523,6 +528,7 @@ def _infer_dtype_from_inputs(inputs):
 @builtin
 class NdEye(CallableTemplate):
     key = numpy.eye
+    return_new_reference = True
 
     def generic(self):
         def typer(N, M=None, k=None, dtype=None):
@@ -541,6 +547,7 @@ def typer(N, M=None, k=None, dtype=None):
 @builtin
 class NdArange(AbstractTemplate):
     key = numpy.arange
+    return_new_reference = True
 
     def generic(self, args, kws):
         assert not kws
@@ -566,6 +573,7 @@ def generic(self, args, kws):
 @builtin
 class NdLinspace(AbstractTemplate):
     key = numpy.linspace
+    return_new_reference = True
 
     def generic(self, args, kws):
         assert not kws


Test patch: