11

I made a small tkinter game that uses turtle for graphics. It's a simulation of the Triangle Peg Game from Cracker Barrel that is able to tell the player the next best move to make at any point in the game, among other features. Pegs are just instances of a subclass of turtle.RawPen, and I keep plenty of plain instances of RawPen around to draw arrows representing moves.

I noticed that when I restart the game (which calls turtle.bye()) to kill the turtle window, that memory consumption actually increases, as turtles don't seem to be deleted. Even if I call window.clear() beforehand, which clears _turtles in window.__dict__, there are still references to the turtles. I ensured that all the references that I make to them are deleted during restart, so that's not the issue. Is there any way to truly delete a turtle so it can be garbage collected?

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Isaac Saffold
  • 1,116
  • 1
  • 11
  • 20
  • I played with this and concluded you have to call both `window.clear()` then `window.bye()` to get individual turtle reference counts to drop to zero. The `.clear()` takes care of `window._turtles` and the `.bye()` takes care of extra references caused by turtle event methods like `onclick()`. Can't say where your additional references are coming from. – cdlane May 15 '17 at 18:43
  • I used `gc.get_referrers()` during debugging on all turtles used (I store them in `peg_dir`, `graveyard`, and `artist_dir`) and the only references were the list containing them, `window._turtles`, and event methods. I've tried clearing all my lists, using `window.clear()`, and then `window.bye()`, and `gc.collect()` says that the objects in `peg_dir` are unreachable. No idea why. I'll look into it. – Isaac Saffold May 15 '17 at 23:10

4 Answers4

2

Deleting all my references to objects in the canvas (including, of course, the TurtleWindow) and then destroying the canvas with canvas.destroy() did the trick. Perhaps there are other solutions, but this was the best that I could think of. I appreciate everyone's help, as it will serve me well in the future, at least with objects not created using the turtle API.

Isaac Saffold
  • 1,116
  • 1
  • 11
  • 20
  • 2
    Could you explain what you did? I am having the same issue. – Gigi Bayte 2 Sep 25 '18 at 06:08
  • I just remembered that someone commented on this. I've been very busy, so sorry for not responding earlier. I'm a bit confused about what you're asking, though. I already described the problem and how I eventually solved it in this answer, my original post, and in comments to other answers. – Isaac Saffold Oct 04 '18 at 01:30
  • No problem! (Warning that this may be a stupid question.) "Deleting all my references to objects in the canvas" How did you do so? – Gigi Bayte 2 Oct 04 '18 at 03:10
  • When I said "my references to objects in the canvas", I meant MY references: assignments that I explicitly made. For example, each instance of my `TurtleGraphics` class has a member called `peg_dir`, a list containing pegs (instances of a subclass of `RawPen` I made) that are currently visible to the player. In this case, a good way to delete the references would be to invoke `peg_dir.clear()`. My problem was that even though I had deleted MY references, I couldn't find a way to get `tkinter`/`turtle` to delete all ITS references. – Isaac Saffold Oct 04 '18 at 03:44
  • That makes sense. Thanks! – Gigi Bayte 2 Oct 04 '18 at 03:49
2

The usual thing to do to get rid of data in turtles is reset():

carl=Turtle()
.... code
carl.reset()

For list of turtles, here kim,donald, fanny and frank are all turtles:

group=[kim,donald,fanny,frank]
for turtle in group:
    turtle.reset()

There is also an convenient code for all turtles on a specific screen, this is a built in list called (screen.turtles). So if You have a screen called screen:

screen=Screen()
...
code
....

for turtle in screen.turtles():
    turtle.reset()
Lars Tuff
  • 127
  • 3
  • 3
    Your `reset()` loop across `screen.turtles()` is effectively what `turtle.resetscreen()` (aka `turtle.Screen().reset()`) does. The significant memory freed (i.e. the OP's question) by `turtle.reset()` is clearing the turtle's **undo** buffer, other memory elements are simply reset to their default values. To help encourage garbage collection of turtles, use `turtle.clearscreen()` (aka `turtle.Screen().clear()`) and set all your turtle containing variables to None. – cdlane Mar 06 '18 at 05:25
  • Perhaps `screen._turtles.remove(turtle)`? I have no idea if it's advisable or not. – Wildcard Jun 12 '20 at 00:17
  • @Wildcard it's an idea, but even with `turtle.reset()`, trying to prune elements from `_turtles` seems to leak memory. There's a test in [this post](https://stackoverflow.com/a/73263441/6243352) but it's possible I messed something up. Needless to say, messing with internals is probably a hacky workaround even if it did work. The same answer suggests an object pool, which seems like a stronger (possibly best?) solution for most use cases. – ggorlen Aug 06 '22 at 22:04
0

I face the same problem I have a list of turtle and I want to delete them each time I loop into the list with:

 turtle.undo()
hardkoded
  • 18,915
  • 3
  • 52
  • 64
Pourya
  • 13
  • 3
-1

Did you try deleting the memory-consuming object and then collecting the garbage explicitly using Python's built-in garbage collector interface?

import gc
...
# Delete memory-consuming object
del window._turtles
# Collect the garbage
gc.collect()
Community
  • 1
  • 1
Josselin
  • 2,593
  • 2
  • 22
  • 35
  • I tried that during debugging and the objects were unreachable, but I'll try again and make absolutely sure all references are gone. I don't see why that would help, though. Shouldn't garbage collection automatically occur anyway if there are no references left, and isn't it impossible to garbage collect if there are references, even explicitly? – Isaac Saffold May 15 '17 at 22:26
  • 1
    I once had a project in which one function made local use of a large Numpy array. I expected this array to be automatically garbage collected upon leaving the function scope, but my CPU usage indicated that Python still kept some hidden references to it... Calling `del array` and then `gc.collect()` before leaving the function enabled me to really free up the memory. – Josselin May 16 '17 at 07:11