pywebview: Memory leak on windows

There appears to be a memory leak on Windows, occurring whenever webview.evaluate_js() is called. I’m using pywebview v1.7 + “pythonnet” on Windows 10 64bit, with python3.6.2. The same issue does not exist on mac.

Code to reproduce (most trivial example I could come up with)

import webview
import threading
import time

threadStopSignal = threading.Event()
threadStopSignal.clear()

def checkOnDOM():
	global threadStopSignal 
	while threadStopSignal.is_set() == False:
		time.sleep(.01)
		webview.evaluate_js("doNothing()" )
		
def main():
	print("Beginning program")
	t = threading.Thread(target=checkOnDOM)
	t.start()

	webview.create_window("This is a window", "index.html")
	threadStopSignal.set()
	t.join(3.0)
main()
<!DOCTYPE html>
<html>
<script type="text/javascript">
	function doNothing() { }
</script>
<head>
</head>
<body>
<div>
</div>
</body>
</html>

Then watch the memory usage of the program grow in the task manager. The memory usage on Mac remains static when running this program.

In order to further investigate, I later changed doNothing to

function doNothing() {
     return document.getElementsByTagName("script").length;
}

which, when printed in python, made it clear that the number of script elements is growing (and never shrinking) with each call to evaluate_js(). Not sure if that’s the expected behavior.

Finally, if the value of those script tags is printed, we see something like function invokecb2c8066aea211e7849010f0050b27cc() {return eval("doNothing()")} which over time monotonically grows to higher values

function invokef9d07746aea211e780ee10f0050b27cc() {return eval("doNothing()")}
function invokef9d29312aea211e791b410f0050b27cc() {return eval("doNothing()")}
function invokef9d46ef6aea211e78f1f10f0050b27cc() {return eval("doNothing()")}

up and up… Are these pointers to resources in windows that never get cleaned up, or are they just GUIDs?

Happy to provide any more info.

Really enjoying the library, btw! Hoping to resolve the memory issue on the windows side so I can use it further.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 26 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Great, thanks for looking into this. I’m happy with this as a result - was just trying to be thorough. Shall we consider this closed?

Here is what I found out.

  1. As it was pointed above, your original program has got an infinite loop. Remember that create_window blocks execution, until the window is destroyed
  2. I have modified the script to invoke doNothing repeatedly without invoking evaluate_js and memory grows in the same fashion as with evaluate_js. Furthermore IE does the same.
<!DOCTYPE html>
<html>
<script type="text/javascript">
	function doNothing() { }

	for(var i =0; i < 100000; i++) {
	    doNothing();
    }
</script>
<head>
</head>
<body>
<div>
    <button onclick="doNothing()">Test</button>
</div>
</body>
</html>
  1. All in all, it seems this is how IE garbage collection works and there is nothing we can do about that. A good thing that we got rid script elements and invoke functions, though!

I pushed a revised version to the master. For some reason it refuses to work with the escaped script, but the original script works like a charm (with line breaks and line comments) @pomplesiegel Could you verify that it does not leak memory?

Hi @shivaprsdv, thanks for the update! I just checked out and ran this branch on windows and unfortunately the memory leak still persists on Windows. I’m happy to try anything further.

I see the problem now, but this is how the Windows implementation works. The problem is that Windows does not provide a way to run arbitrary Javascript, but instead is able to run a named function. So in order to run Javascript code, it must be wrapped into a named function, which will be called (wrapped inside an eval to get its return value). Given all that, the function will stay in memory. I guess we could fit a del after the function call. I will investigate if it is a feasible way and how it will play with async code.