Skip to content

Python bindings: returned vertex/zone Arrays are never freed #22

Description

@ajfriend

In the Python bindings, the eC Array returned by getZoneRefinedWGS84Vertices is never freed, so long-running processes leak memory on every call. getZoneWGS84Vertices and listZones leak the same way. The cause seems to be that the wrapper classes have a delete() method but no __del__, so Python's garbage collector never releases the native side of any object the bindings create (GeoPoint, etc.).

import resource
import sys

import dggal as al
al.pydggal_setup(al.Application())


def mem_usage_mb():
    """Peak memory usage (max RSS) of this process, in MB."""
    scale = 1 if sys.platform == 'darwin' else 1024  # ru_maxrss: bytes on macOS, KB on Linux
    return resource.getrusage(resource.RUSAGE_SELF).ru_maxrss * scale / 2**20


isea = al.ISEA3H()
z = isea.getZoneFromWGS84Centroid(8, al.GeoPoint(42.1, 12.0))

print(f'     0 calls  mem = {mem_usage_mb():5.0f} MB')
for i in range(1, 50_001):
    isea.getZoneRefinedWGS84Vertices(z, 20)
    if i % 10_000 == 0:
        print(f'{i:6d} calls  mem = {mem_usage_mb():5.0f} MB')

Output:

     0 calls  mem =    28 MB
 10000 calls  mem =    60 MB
 20000 calls  mem =   100 MB
 30000 calls  mem =   137 MB
 40000 calls  mem =   139 MB
 50000 calls  mem =   193 MB

The workaround is explicitly freeing each returned array after copying the values out. However, arr.delete() doesn't seem to work, but ecrt.Instance.delete(arr) does. But maybe I'm doing something wrong?

The workaround comes with a tradeoff. The delete itself slows down dramatically as allocations accumulate (I can file another issue on that if it's helpful), so in bulk workloads it trades the leak for a slowdown.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions