Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfb84cd2

Browse files
authored
Merge pull request#704 from williamsardar/master
Fix memory leaks due to spurious handle increment and add unit tests
2 parents611814c +a97a776 commitfb84cd2

File tree

8 files changed

+158
-6
lines changed

8 files changed

+158
-6
lines changed

‎AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
- Ville M. Vainio ([@vivainio](https://github.com/vivainio))
4343
- Virgil Dupras ([@hsoft](https://github.com/hsoft))
4444
- Wenguang Yang ([@yagweb](https://github.com/yagweb))
45+
- William Sardar ([@williamsardar])(https://github.com/williamsardar)
4546
- Xavier Dupré ([@sdpython](https://github.com/sdpython))
4647
- Zane Purvis ([@zanedp](https://github.com/zanedp))
4748
- ([@bltribble](https://github.com/bltribble))

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
3535
- Fixed`LockRecursionException` when loading assemblies ([#627][i627])
3636
- Fixed errors breaking .NET Remoting on method invoke ([#276][i276])
3737
- Fixed PyObject.GetHashCode ([#676][i676])
38+
- Fix memory leaks due to spurious handle incrementation ([#691][i691])
3839

3940

4041
##[2.3.0][] - 2017-03-11

‎requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Requirements for both Travis and AppVeyor
22
pytest==3.2.5
33
coverage
4+
psutil
45

56
# Coverage upload
67
codecov

‎src/runtime/constructorbinding.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static IntPtr tp_descr_get(IntPtr op, IntPtr instance, IntPtr owner)
7575
return Exceptions.RaiseTypeError("How in the world could that happen!");
7676
}
7777
}*/
78-
Runtime.XIncref(self.pyHandle);// Decref'd by the interpreter.
78+
Runtime.XIncref(self.pyHandle);
7979
returnself.pyHandle;
8080
}
8181

@@ -105,8 +105,6 @@ public static IntPtr mp_subscript(IntPtr op, IntPtr key)
105105
}
106106
varboundCtor=newBoundContructor(self.type,self.pyTypeHndl,self.ctorBinder,ci);
107107

108-
/* Since nothing is cached, do we need the increment???
109-
Runtime.XIncref(boundCtor.pyHandle); // Decref'd by the interpreter??? */
110108
returnboundCtor.pyHandle;
111109
}
112110

‎src/runtime/methodbinding.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx)
5656
}
5757

5858
varmb=newMethodBinding(self.m,self.target){info=mi};
59-
Runtime.XIncref(mb.pyHandle);
6059
returnmb.pyHandle;
6160
}
6261

@@ -85,7 +84,6 @@ public static IntPtr tp_getattro(IntPtr ob, IntPtr key)
8584
case"__overloads__":
8685
case"Overloads":
8786
varom=newOverloadMapper(self.m,self.target);
88-
Runtime.XIncref(om.pyHandle);
8987
returnom.pyHandle;
9088
default:
9189
returnRuntime.PyObject_GenericGetAttr(ob,key);

‎src/runtime/overload.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public static IntPtr mp_subscript(IntPtr tp, IntPtr idx)
4444
}
4545

4646
varmb=newMethodBinding(self.m,self.target){info=mi};
47-
Runtime.XIncref(mb.pyHandle);
4847
returnmb.pyHandle;
4948
}
5049

‎src/testing/methodtest.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,3 +666,23 @@ public string PublicMethod(string echo)
666666
}
667667
}
668668
}
669+
670+
namespacePlainOldNamespace
671+
{
672+
publicclassPlainOldClass
673+
{
674+
publicPlainOldClass(){}
675+
676+
publicPlainOldClass(intparam){}
677+
678+
privatereadonlybyte[]payload=newbyte[(int)Math.Pow(2,20)];//1 MB
679+
680+
publicvoidNonGenericMethod(){}
681+
682+
publicvoidGenericMethod<T>(){}
683+
684+
publicvoidOverloadedMethod(){}
685+
686+
publicvoidOverloadedMethod(intparam){}
687+
}
688+
}

‎src/tests/test_method.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,3 +832,137 @@ def test_case_sensitive():
832832

833833
withpytest.raises(AttributeError):
834834
MethodTest.casesensitive()
835+
836+
deftest_getting_generic_method_binding_does_not_leak_ref_count():
837+
"""Test that managed object is freed after calling generic method. Issue #691"""
838+
839+
fromPlainOldNamespaceimportPlainOldClass
840+
841+
importsys
842+
843+
refCount=sys.getrefcount(PlainOldClass().GenericMethod[str])
844+
assertrefCount==1
845+
846+
deftest_getting_generic_method_binding_does_not_leak_memory():
847+
"""Test that managed object is freed after calling generic method. Issue #691"""
848+
849+
fromPlainOldNamespaceimportPlainOldClass
850+
851+
importpsutil,os,gc,clr
852+
853+
process=psutil.Process(os.getpid())
854+
processBytesBeforeCall=process.memory_info().rss
855+
print("\n\nMemory consumption (bytes) at start of test: "+str(processBytesBeforeCall))
856+
857+
iterations=500
858+
foriinrange(iterations):
859+
PlainOldClass().GenericMethod[str]
860+
861+
gc.collect()
862+
clr.System.GC.Collect()
863+
864+
processBytesAfterCall=process.memory_info().rss
865+
print("Memory consumption (bytes) at end of test: "+str(processBytesAfterCall))
866+
processBytesDelta=processBytesAfterCall-processBytesBeforeCall
867+
print("Memory delta: "+str(processBytesDelta))
868+
869+
bytesAllocatedPerIteration=pow(2,20)# 1MB
870+
bytesLeakedPerIteration=processBytesDelta/iterations
871+
872+
# Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration
873+
failThresholdBytesLeakedPerIteration=bytesAllocatedPerIteration/2
874+
875+
assertbytesLeakedPerIteration<failThresholdBytesLeakedPerIteration
876+
877+
deftest_getting_overloaded_method_binding_does_not_leak_ref_count():
878+
"""Test that managed object is freed after calling overloaded method. Issue #691"""
879+
880+
fromPlainOldNamespaceimportPlainOldClass
881+
882+
importsys
883+
884+
refCount=sys.getrefcount(PlainOldClass().OverloadedMethod.Overloads[int])
885+
assertrefCount==1
886+
887+
deftest_getting_overloaded_method_binding_does_not_leak_memory():
888+
"""Test that managed object is freed after calling overloaded method. Issue #691"""
889+
890+
fromPlainOldNamespaceimportPlainOldClass
891+
892+
importpsutil,os,gc,clr
893+
894+
process=psutil.Process(os.getpid())
895+
processBytesBeforeCall=process.memory_info().rss
896+
print("\n\nMemory consumption (bytes) at start of test: "+str(processBytesBeforeCall))
897+
898+
iterations=500
899+
foriinrange(iterations):
900+
PlainOldClass().OverloadedMethod.Overloads[int]
901+
902+
gc.collect()
903+
clr.System.GC.Collect()
904+
905+
processBytesAfterCall=process.memory_info().rss
906+
print("Memory consumption (bytes) at end of test: "+str(processBytesAfterCall))
907+
processBytesDelta=processBytesAfterCall-processBytesBeforeCall
908+
print("Memory delta: "+str(processBytesDelta))
909+
910+
bytesAllocatedPerIteration=pow(2,20)# 1MB
911+
bytesLeakedPerIteration=processBytesDelta/iterations
912+
913+
# Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration
914+
failThresholdBytesLeakedPerIteration=bytesAllocatedPerIteration/2
915+
916+
assertbytesLeakedPerIteration<failThresholdBytesLeakedPerIteration
917+
918+
deftest_getting_method_overloads_binding_does_not_leak_ref_count():
919+
"""Test that managed object is freed after calling overloaded method. Issue #691"""
920+
921+
fromPlainOldNamespaceimportPlainOldClass
922+
923+
importsys
924+
925+
refCount=sys.getrefcount(PlainOldClass().OverloadedMethod.Overloads)
926+
assertrefCount==1
927+
928+
deftest_getting_method_overloads_binding_does_not_leak_memory():
929+
"""Test that managed object is freed after calling overloaded method. Issue #691"""
930+
931+
fromPlainOldNamespaceimportPlainOldClass
932+
933+
importpsutil,os,gc,clr
934+
935+
process=psutil.Process(os.getpid())
936+
processBytesBeforeCall=process.memory_info().rss
937+
print("\n\nMemory consumption (bytes) at start of test: "+str(processBytesBeforeCall))
938+
939+
iterations=500
940+
foriinrange(iterations):
941+
PlainOldClass().OverloadedMethod.Overloads
942+
943+
gc.collect()
944+
clr.System.GC.Collect()
945+
946+
processBytesAfterCall=process.memory_info().rss
947+
print("Memory consumption (bytes) at end of test: "+str(processBytesAfterCall))
948+
processBytesDelta=processBytesAfterCall-processBytesBeforeCall
949+
print("Memory delta: "+str(processBytesDelta))
950+
951+
bytesAllocatedPerIteration=pow(2,20)# 1MB
952+
bytesLeakedPerIteration=processBytesDelta/iterations
953+
954+
# Allow 50% threshold - this shows the original issue is fixed, which leaks the full allocated bytes per iteration
955+
failThresholdBytesLeakedPerIteration=bytesAllocatedPerIteration/2
956+
957+
assertbytesLeakedPerIteration<failThresholdBytesLeakedPerIteration
958+
959+
deftest_getting_overloaded_constructor_binding_does_not_leak_ref_count():
960+
"""Test that managed object is freed after calling overloaded constructor, constructorbinding.cs mp_subscript. Issue #691"""
961+
962+
fromPlainOldNamespaceimportPlainOldClass
963+
964+
importsys
965+
966+
# simple test
967+
refCount=sys.getrefcount(PlainOldClass.Overloads[int])
968+
assertrefCount==1

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp