Same memory leaks. Now tested under python 2.#270
Same memory leaks. Now tested under python 2.#270Adriandorr wants to merge 8 commits intoultrajson:mainfrom
Conversation
Using ujson.dumps() with objects having __json__() method leaked memory for object's JSON representation.
PyObject_CallObject() returns a PyObject*; discarding it leaked memory for the result of output.write().
Merged pull request to fix memory leak.
|
I can confirm this works, hope it'll get pulled upstream. |
| if (GET_TC(tc)->itemName) { | ||
| Py_DECREF(GET_TC(tc)->itemName); | ||
| GET_TC(tc)->itemName = NULL; | ||
| } |
There was a problem hiding this comment.
Accidental extra indentation here.
There was a problem hiding this comment.
ping @Adriandorr , this looks like the last remaining problem..?
tests/tests.py
Outdated
|
|
||
| def test_ujson_has_no_memory_leak(self): | ||
| import gc | ||
| import objgraph |
There was a problem hiding this comment.
The new dependency should be added to .travis.yml.
|
Nice work. This pr fixes memory leaks when Some examples I can confirm:
|
|
Thank you, this is a helpful fix. Hope it can be merged soon! |
|
@Adriandorr I am not able to reproduce these memory leaks with ujson(1.35) and python(2.7.14). I have used tests added by you in this fix. Reference count before and after was same. Here is the script that I used: import ujson
import sys
def test_does_not_leak_dictionary_values():
import gc
gc.collect()
value = ["abc"]
data = {"1": value}
ref_count = sys.getrefcount(value)
ujson.dumps(data)
print(ref_count, sys.getrefcount(value))
def test_does_not_leak_dictionary_keys():
import gc
gc.collect()
key1 = "1"
key2 = "1"
value1 = ["abc"]
value2 = [1,2,3]
data = {key1: value1, key2: value2}
ref_count1 = sys.getrefcount(key1)
ref_count2 = sys.getrefcount(key2)
ujson.dumps(data)
print(ref_count1, sys.getrefcount(key1))
print(ref_count2, sys.getrefcount(key2))
def test_does_not_leak_dictionary_string_key():
import gc
gc.collect()
key1 = "1"
value1 = 1
data = {key1: value1}
ref_count1 = sys.getrefcount(key1)
ujson.dumps(data)
print(ref_count1, sys.getrefcount(key1))
def test_does_not_leak_dictionary_tuple_key():
import gc
gc.collect()
key1 = ("a",)
value1 = 1
data = {key1: value1}
ref_count1 = sys.getrefcount(key1)
ujson.dumps(data)
print(ref_count1, sys.getrefcount(key1))
def test_does_not_leak_dictionary_bytes_key():
import gc
gc.collect()
key1 = b"1"
value1 = 1
data = {key1: value1}
ref_count1 = sys.getrefcount(key1)
ujson.dumps(data)
print(ref_count1, sys.getrefcount(key1))
def test_does_not_leak_dictionary_None_key():
import gc
gc.collect()
key1 = None
value1 = 1
data = {key1: value1}
ref_count1 = sys.getrefcount(key1)
ujson.dumps(data)
print(ref_count1, sys.getrefcount(key1))
test_does_not_leak_dictionary_values()
test_does_not_leak_dictionary_keys()
test_does_not_leak_dictionary_string_key()
test_does_not_leak_dictionary_tuple_key()
test_does_not_leak_dictionary_bytes_key()
test_does_not_leak_dictionary_None_key() |
|
Have you tried adding the tests from the pull requests without the fixes? |
|
Yes I have tested it without fixes. I am using released version 1.35 which
doesn't have these fixes.
Naveen
…On Wed 24 Apr, 2019, 20:52 Adrian Dörr, ***@***.***> wrote:
Have you tried adding the tests from the pull requests without the fixes?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#270 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ABFOYQHBKVUXZNQJ43YKAZDPSB3MNANCNFSM4DPXVE5Q>
.
|
|
Strange. It’s been a while since I used this. I think it was with python 3, but might well be wrong. |
| if (GET_TC(tc)->itemName) { | ||
| Py_DECREF(GET_TC(tc)->itemName); | ||
| GET_TC(tc)->itemName = NULL; | ||
| } |
There was a problem hiding this comment.
ping @Adriandorr , this looks like the last remaining problem..?
Port of ultrajson/ultrajson#270 All credits to @Adriandorr
|
|
||
| if (!(GET_TC(tc)->itemValue = PyObject_GetItem(GET_TC(tc)->dictObj, GET_TC(tc)->itemName))) | ||
| { | ||
| if (GET_TC(tc)->itemValue) { |
There was a problem hiding this comment.
Oh, I hadn't seen this and fixed it differently in #353.
|
I can reproduce the test failures locally on this branch, it's not always the same failure, and sometimes it passes: $ python3.6 tests/tests.py
................................................................E...............................................................
======================================================================
ERROR: test_does_not_leak_dictionary_None_key (__main__.UltraJSONTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests/tests.py", line 917, in test_does_not_leak_dictionary_None_key
ujson.dumps(data)
OverflowError: Unterminated UTF-8 sequence when encoding string
----------------------------------------------------------------------
Ran 128 tests in 0.887s
FAILED (errors=1)
$ python3.6 tests/tests.py
................................................................E...............................................................
======================================================================
ERROR: test_does_not_leak_dictionary_None_key (__main__.UltraJSONTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests/tests.py", line 917, in test_does_not_leak_dictionary_None_key
ujson.dumps(data)
OverflowError: Unterminated UTF-8 sequence when encoding string
----------------------------------------------------------------------
Ran 128 tests in 1.156s
FAILED (errors=1)
$ python3.6 tests/tests.py
................................................................E...............................................................
======================================================================
ERROR: test_does_not_leak_dictionary_None_key (__main__.UltraJSONTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "tests/tests.py", line 917, in test_does_not_leak_dictionary_None_key
ujson.dumps(data)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xbb in position 9: invalid start byte
----------------------------------------------------------------------
Ran 128 tests in 0.625s
FAILED (errors=1)
$ python3.6 tests/tests.py
................................................................................................................................
----------------------------------------------------------------------
Ran 128 tests in 0.892s
OK
$They pass when using pytest, it must enforce better test isolation: $ mv tests/tests.py tests/test_ujson.py
$ python3.6 -m pytest
==================================================== test session starts ====================================================
platform darwin -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /Users/hugo/github/ultrajson
plugins: cov-2.8.1
collected 128 items
tests/test_ujson.py ................................................................................................. [ 75%]
............................... [100%]
==================================================== 128 passed in 1.46s ====================================================
$ python3.6 -m pytest
==================================================== test session starts ====================================================
platform darwin -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /Users/hugo/github/ultrajson
plugins: cov-2.8.1
collected 128 items
tests/test_ujson.py ................................................................................................. [ 75%]
............................... [100%]
==================================================== 128 passed in 2.08s ====================================================
$ python3.6 -m pytest
==================================================== test session starts ====================================================
platform darwin -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /Users/hugo/github/ultrajson
plugins: cov-2.8.1
collected 128 items
tests/test_ujson.py ................................................................................................. [ 75%]
............................... [100%]
==================================================== 128 passed in 1.41s ====================================================
$I've created PR #363 to switch over to pytest. |
|
Using pytest will fix it for 3.6 on Linux and macOS, but it still fails for 3.5 on Linux:
@Adriandorr Please could you check it? |
|
@nav-agarwal Thanks for providing those tests! Thanks to them, I was able to confirm our upgrade to For others stumbling here, see code in this gist: https://gist.github.com/AndreLouisCaron/a18ae434e2eaf2866ffa4a24f693b2e8 to run tests against multiple Note that I have the same results whether I use |
|
Closing this as it's very much out of date but new PRs always welcome to fix things like this (and there have been quite a few recently). Thanks! |
This contains a fixes for a number of memory leaks, mainly related to dictionary keys and values.