Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

Snapshot object has memory leak. Maybe...
Started by sjmuse Nov 28 2013 06:51 PM

22 replies to this topic
[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

sjmuse

[GLOBAL: userInfoPane.html]
sjmuse
  • Observer

  • 7 posts
  • Corona SDK

local storyboard = require("storyboard")
storyboard.isDebug = true
timer.performWithDelay(1000, function (e)
	storyboard:printMemUsage()
end, 0)

timer.performWithDelay(1000, function (e)
	local snapshot = display.newSnapshot(100, 100)
	snapshot:removeSelf()
	snapshot = nil	
end, 0)
I found memory increase in my project.
 
I has spent many hours for finding memory leak and I found snapshot has memory leak.
 
How can i clean up snapshot?

 



[TOPIC: post.html]
#2

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

This is because garbage collection doesn't happen right away.  Look at this code instead:
 

local storyboard = require("storyboard")
storyboard.isDebug = true
storyboard:printMemUsage()

local ss = {}

for i = 1, 100 do
    ss[i] = display.newSnapshot(100,100)
end

for i = 100, 1, -1 do
    ss[i]:removeSelf()
    ss[i] = nil
end

storyboard:printMemUsage()

timer.performWithDelay(10000, function() storyboard:printMemUsage(); end, 1)

 

When it first runs, I have 263K of Lua memory used and 0 texture memory.

After creating and deleting 100 snapshots, the Lua memory has increased to 329K (probably the increase in the table allocation) and I have 5.376 megabytes of texture memory, which makes sense give that we've created 100 128x128x 4 textures.  But even though we've blown through the table and cleared it, garbage collection has not run yet, so the texture memory is still in use.

 

Upon finish, I see that my Lua memory has dropped a little bit from 329K to 313K. This is likely nil'ing the odd things in the table (but the table records still exist).  After 10 seconds we print the memory again after garbage collection has has time to run and my texture memory is back down to a lovely 0.0 where it started.

 

It's really hard to use the tests like you're doing to look for leaks.  It's natural for your data to grow and shrink over time.  That's not necessarily a leak.  Because garbage collection happens behind the scene it makes these tests

 

Rob.



[TOPIC: post.html]
#3

sjmuse

[GLOBAL: userInfoPane.html]
sjmuse
  • Observer

  • 7 posts
  • Corona SDK

local storyboard = require("storyboard")
storyboard.isDebug = true
storyboard:printMemUsage()

local ss = {}

for i = 1, 5000 do
    ss[i] = display.newSnapshot(4,4)
end
storyboard:printMemUsage()
for i = 5000, 1, -1 do
    ss[i]:removeSelf()
    ss[i] = nil
end
storyboard:printMemUsage()
timer.performWithDelay(3000, function() storyboard:printMemUsage(); end, 0)

I still doubt snapshot memory management .

 

Texture memory management is perfect but system memory is not perfect.

 

This sample makes garbage over 1Mbyte of system memory. Never garbage collected.

 

Of course, this sample is not a normally case but it proves that snapshot has some system memory problem.

 

If use other display object(image, container..etc), system memory is clean up perfectly.

 

 



[TOPIC: post.html]
#4

sjmuse

[GLOBAL: userInfoPane.html]
sjmuse
  • Observer

  • 7 posts
  • Corona SDK

Anyway, thanks for reply. Please make resolve this issue.



[TOPIC: post.html]
#5

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

This block of code:

[code=auto:0]

for i = 1, 5000 do
    ss[i] = display.newSnapshot(4,4)
end

[code=auto:0]

 

creates a lua table that is 5000 entries big.  Even though you remove the objects and nil them, the table still holds 5000 nil's.  If you do:

 

ss = nil

 

after you're done freeing things up to blow away the table, does it shrink back down?  It may not shrink all the way because there may be meta tables or other things that Lua and API calls are doing under the hood.

 

This line:  timer.performWithDelay(3000, function() storyboard:printMemUsage(); end, 0)

 

is also creating a timer object which is still live and functioning while that function is running.  It's really hard to try and look at memory management using examples like this.  They are not realistic usage and frequently what looks like leaks really are not leaks. 

 

Rob



[TOPIC: post.html]
#6

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

Sorry to use the old post, But the problem seems still exist.
 

I use the following code to test:

for i = 1, 1000 do
    local snapshot = display.newSnapshot(100, 100)
    display.remove(snapshot)
    snapshot = nil
end

And the result:

 

Dec 24 18:33:56.994: memUsage = 821.044 KB

Dec 24 18:33:56.995: texMemUsage = 10.004 MB

Dec 24 18:33:58.002: memUsage = 774.126 KB

Dec 24 18:33:58.002: texMemUsage = 10.004 MB

Dec 24 18:33:59.003: memUsage = 1260.154 KB

Dec 24 18:33:59.003: texMemUsage = 10.004 MB

Dec 24 18:34:00.019: memUsage = 1213.279 KB

Dec 24 18:34:00.019: texMemUsage = 10.004 MB

Dec 24 18:34:01.027: memUsage = 1213.279 KB

Dec 24 18:34:01.027: texMemUsage = 10.004 MB

Dec 24 18:34:02.035: memUsage = 1213.279 KB

Dec 24 18:34:02.035: texMemUsage = 10.004 MB

Dec 24 18:34:03.042: memUsage = 1213.279 KB

Dec 24 18:34:03.042: texMemUsage = 10.004 MB

Dec 24 18:34:04.051: memUsage = 1213.279 KB

Dec 24 18:34:04.051: texMemUsage = 10.004 MB

Dec 24 18:34:05.060: memUsage = 1635.154 KB

Dec 24 18:34:05.061: texMemUsage = 10.004 MB

Dec 24 18:34:06.068: memUsage = 1588.279 KB

Dec 24 18:34:06.068: texMemUsage = 10.004 MB

Dec 24 18:34:07.077: memUsage = 2138.154 KB

Dec 24 18:34:07.077: texMemUsage = 10.004 MB

Dec 24 18:34:08.084: memUsage = 2091.279 KB

Dec 24 18:34:08.084: texMemUsage = 10.004 MB

Dec 24 18:34:09.092: memUsage = 2091.279 KB

Dec 24 18:34:09.092: texMemUsage = 10.004 MB

 

 
The memory usage is increase every time when I run the above "for" loop. although it will release some memory after 1-2 second.
If I replace the "newSnapshot" to "newGroup", no memory leak occur.


[TOPIC: post.html]
#7

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

You cannot do these kinds of tests to "prove" a leak.  This simply isn't how Lua's garbage collection works.  Garbage collection runs in intervals and it's not a fixed amount of time, there are many factors that trigger when Lua cleans up memory.  These type of "print memory usage" things only works over a long period of time and can't be used to measure if one API call is leaking memory.

 

Rob



[TOPIC: post.html]
#8

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

Thanks for your reply in Christmas. And sorry for the wordings that I descript the issue :)
Is there any way to confirm that the memory will release?
For this, I have been tested with "collectgarbage" after the for loop, though it may not be the correct method. No memory release after collectgarbage.
And I tested with display group with object on it. The memory seems will release after 1-2 second.

By the way, one more suspected case is the nested object:
Scroll view -> display group -> scroll view

local baseScrollView = widget.newScrollView(viewOption)
local group = display.newGroup()
baseScrollView:insert(group)
local childScrollView = widget.newScrollView(viewOption2)
group:insert(childScrollView)

Sorry for just a part of code since I'm using mobile to reply.

If I just remove the base scroll view, the memory seems will not release. But if I remove the display group first, all created memory will be cleared.

Just want to make sure if the objects works normally before release the app.

Thanks and Merry Christmas

[TOPIC: post.html]
#9

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

When I built my first Corona SDK based app, I lost a lot of sleep and stressed out over memory leaks.  I didn't want my first app rejected and I did a lot of what you're doing.  It takes time to learn what causes leaks and how to avoid them.  

 

You also have to have faith that Lua and Objective-C/Java are going to do the right thing for you.  One of the nice things about using a high level language like Lua vs. a lower level language like C is that you don't have to manage your memory.  As long as you remove things when your done and nil them, the system will take care of them.

 

So many people do a tight loop test to try and prove a leak in Corona code and in 100% of the cases, the test isn't taking Lua's garbage collection schedule in to consideration.  It's also an unrealistic way people use the code.  No one is going to make 1000 snapshots in a fraction of a second. 

 

What you should be worried about is the long term memory usage.  If you are not creating new things or you think you are disposing of everything properly, is your memory growing over time.

 

Rob



[TOPIC: post.html]
#10

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

Before reply your message, I want to tell you more about what I'm doing:
I'm writing an post-base app, which will have lots of post display in scrollview. In order to understand the performance, I make a test with lots of post, let's say, 1000 post, in the scrollview. After that, I need to know if I can remove all I created, so I run the remove objects and see if the memory usage is correct. But it failed. So I minimize the factors and find out that the problem may come from here. That's why I post a question.

Sorry that my following reply maybe a little bit harsh.

For the second paragraph. I have faith that lua will help me to manage the memory correctly, so what I did first is to find out if I did anything wrong. But even I minimized the case, the problem is still here. So I asked here to see if I can have more evidence to prove that I can have "faith" on the system. A programmer should have a responsibility to find out if somewhere may have critical issue, but not in faith. For a user of Corona, it is also a responsibility to tell the developer of Corona that the potential problem may have. As a programmer, we cannot use "faith" as an only attitude.
Besides, I cannot make sure I can remove all I created if I don't know which memory is created by me.

For the third paragraph. I'm not sure if I have enough consideration on garbage collection. I tried use function "collectGarbage", but no use. I tried to waited for 5 minutes to see if the memory will release, but no use. That's why I asked here to see if still anything I can do. By the way, I cannot find any relationship between the memory leak and the so-called "UNREALISTIC WAY TO USE THE CODE". My intention is to test the performance if lots of objects on the screen. If too much objects is created in the same time will cause problem, I can try to create each object in every second, or even every minute. I just want to make sure if my application will works without critical issue.

Again sorry for my harsh comment. Hope you will understand why I reply in this way. And thanks for your sharing in the first paragraph and the useful advice in the fourth paragraph :)

[TOPIC: post.html]
#11

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

Please advice about the test.

 

For 7 days running of the following code:

local function garbagePrinting()collectgarbage("collect")
local memUsage_str = string.format( "memUsage = %.3f KB", collectgarbage( "count" ) )
print( memUsage_str )
local texMemUsage_str = system.getInfo( "textureMemoryUsed" )
texMemUsage_str = texMemUsage_str/1000
texMemUsage_str = string.format( "texMemUsage = %.3f MB", texMemUsage_str )
print( texMemUsage_str )
end


-- Runtime:addEventListener( "enterFrame", garbagePrinting )


local testSnapShot = {}


local function createNewSnapshot()
for i = 1, 10 do
display.remove(testSnapShot[i])
testSnapShot[i] = display.newSnapshot(100, 100)
local img = display.newImageRect("Image/img1.jpg", 200, 200)
testSnapShot[i].group:insert(img)
testSnapShot[i]:invalidate()
end
end


createNewSnapshot()


timer.performWithDelay(1000, createNewSnapshot, 0)
timer.performWithDelay(1000, garbagePrinting, 0)

gives the following result:

 

Jan  6 12:37:17.237: memUsage = 990749.360 KB

Jan  6 12:37:17.238: texMemUsage = 3040.000 MB
Jan  6 12:37:18.484: memUsage = 990762.485 KB
Jan  6 12:37:18.484: texMemUsage = 3040.000 MB
Jan  6 12:37:19.732: memUsage = 990760.610 KB
Jan  6 12:37:19.733: texMemUsage = 3040.000 MB
Jan  6 12:37:20.967: memUsage = 990773.735 KB
Jan  6 12:37:20.967: texMemUsage = 3040.000 MB
Jan  6 12:37:22.220: memUsage = 990771.860 KB
Jan  6 12:37:22.220: texMemUsage = 3040.000 MB
Jan  6 12:37:23.477: memUsage = 990784.985 KB
Jan  6 12:37:23.477: texMemUsage = 3040.000 MB
Jan  6 12:37:24.714: memUsage = 990783.110 KB
Jan  6 12:37:24.714: texMemUsage = 3040.000 MB
Jan  6 12:37:25.955: memUsage = 990796.235 KB
Jan  6 12:37:25.955: texMemUsage = 3040.000 MB
Jan  6 12:37:27.182: memUsage = 990794.360 KB
Jan  6 12:37:27.182: texMemUsage = 3040.000 MB
Jan  6 12:37:28.426: memUsage = 990807.485 KB
Jan  6 12:37:28.426: texMemUsage = 3040.000 MB
Jan  6 12:37:29.682: memUsage = 990805.610 KB
Jan  6 12:37:29.682: texMemUsage = 3040.000 MB
Jan  6 12:37:30.921: memUsage = 990819.025 KB
Jan  6 12:37:30.922: texMemUsage = 3040.000 MB
Jan  6 12:37:32.161: memUsage = 990816.860 KB
Jan  6 12:37:32.161: texMemUsage = 3040.000 MB
Jan  6 12:37:33.398: memUsage = 990829.985 KB
Jan  6 12:37:33.398: texMemUsage = 3040.000 MB
Jan  6 12:37:34.639: memUsage = 990828.110 KB
Jan  6 12:37:34.639: texMemUsage = 3040.000 MB
Jan  6 12:37:35.906: memUsage = 990841.235 KB
Jan  6 12:37:35.907: texMemUsage = 3040.000 MB
Jan  6 12:37:37.152: memUsage = 990839.360 KB
Jan  6 12:37:37.153: texMemUsage = 3040.000 MB
Jan  6 12:37:38.395: memUsage = 990852.485 KB
Jan  6 12:37:38.396: texMemUsage = 3040.000 MB
Jan  6 12:37:39.652: memUsage = 990850.610 KB
Jan  6 12:37:39.652: texMemUsage = 3040.000 MB
Jan  6 12:37:40.891: memUsage = 990863.735 KB
Jan  6 12:37:40.892: texMemUsage = 3040.000 MB


[TOPIC: post.html]
#12

sjmuse

[GLOBAL: userInfoPane.html]
sjmuse
  • Observer

  • 7 posts
  • Corona SDK

I gave up. :ph34r:

Anyway, we will not use the feature.

He is looking at us as newbie worker, I trust Lua garbage collect more than his words.



[TOPIC: post.html]
#13

khanh.dq

[GLOBAL: userInfoPane.html]
khanh.dq
  • Contributor

  • 264 posts
  • Corona SDK

I tried his code and really think that Corona has problem with display.newSnapshot

I simplified his code like that:

        local function garbagePrinting()
		collectgarbage("collect")
		local memUsage_str = string.format( "memUsage = %.3f KB", collectgarbage( "count" ) )
		print( memUsage_str )
	end

	timer.performWithDelay(2000,function()
		print("before create ------------------------")
		garbagePrinting()
		local a = display.newSnapshot(200,200)
		print("after create ---")
		garbagePrinting()
		timer.performWithDelay(1000,function()
			print("before remove ---")
			garbagePrinting()
			a:removeSelf()
			a = nil
			print("after remove ---")
			garbagePrinting()
			timer.performWithDelay(500,function()
				print("long after remove ---")
				garbagePrinting()
				print (" +++ ")
			end)
		end)
	end,0)

and here is the result: 

qSTGrbH.jpg

You can see memory slowly increase. You just need to modify line 11 from:

local a = display.newSnapshot(200,200)

to 

local a = display.newGroup()

and see no memory increase.

If it's not a Lua leak, so what is difference between display.newSnapshot and display.newGroup ? Did I remove snapshot in the right way with removeSelf function?



[TOPIC: post.html]
#14

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,205 posts
  • Enterprise


local oneSecond = 1000
local tenSeconds = 10000



local function oncePerSecondMemoryCleanupAndReporting()
	--
	-- perform two full garbage collection cycles
	--
	collectgarbage("collect")
	collectgarbage("collect")
	--
	-- report memory usage
	--
	local mem = collectgarbage("count")
	print("Lua memory use: " .. mem .. "K")
end
timer.performWithDelay( oneSecond, oncePerSecondMemoryCleanupAndReporting, 0 )



local theSnapshot = nil
local function oncePerTenSecondsCreateOrRemoveSnapshot()
	--
	-- alternately create OR destroy the snapshot
	--
	if (theSnapshot == nil) then
		theSnapshot = display.newSnapshot(100, 100)
		print("-- SNAPSHOT CREATED --")
	else
		display.remove(theSnapshot)
		theSnapshot = nil
		print("-- SNAPSHOT REMOVED -- ")
	end
end
timer.performWithDelay( tenSeconds, oncePerTenSecondsCreateOrRemoveSnapshot, 0 )


--
-- report a rough initial memory usage (to define those fns, create the timers, etc)
--
collectgarbage("collect")
collectgarbage("collect")
local mem = collectgarbage("count")
print("Initial/Baseline Lua memory use: " .. mem .. "K")


--------------------------------------------------------------------------------
--
-- RUN FOR AT LEAST 100 FULL CREATE/REMOVE CYCLES
--

-- what you'd like to see:
--   all reports following removal stabilize toward some non-increasing value
--
-- what you're likely to see:
--   creating the snapshot consumes about .4K
--   removing the snapshot releases about .2K
--   thus gradual leak of about .2K per create/remove cycle



[TOPIC: post.html]
#15

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

The bug report for this is:  Case 38123

 

Rob



[TOPIC: post.html]
#16

khanh.dq

[GLOBAL: userInfoPane.html]
khanh.dq
  • Contributor

  • 264 posts
  • Corona SDK

The bug report for this is:  Case 38123

 

Rob

 

Thanks, Rob



[TOPIC: post.html]
#17

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

sjmuse: I know that feel :lol:

 

khanh.dq: This is what I found before. Group does not have problem, but snapshot has.

 

Anyway, thanks for your help, khanh.dq and davebollinger

 

Rob: Thanks. By the way, can I use "collectGarbage" instead of waiting?



[TOPIC: post.html]
#18

khanh.dq

[GLOBAL: userInfoPane.html]
khanh.dq
  • Contributor

  • 264 posts
  • Corona SDK

 I think the answer is No. The problem is display.newSnapshot function, not Lua leak. And collectGarbage only works with Lua. And more, collectGarbage auto be called by Lua. You just need to do it manually in some special case: maybe when you remove a thousand objects and need to create another thousand immediately, so you can call collectGarbage before do that. Otherwise, you really don't need to work with collectGarbage.

 

 You can still use snapshot and wait for new Corona DailyBuild fixed it before releasing your game

 

 Btw, when you have a problem and need to report, keep your code's as simple as possible. Like that, you don't need to use table and create too much snapshots, so everyone may think that the memory problem is somewhere with those tables :) . Just focus on snapshot



[TOPIC: post.html]
#19

raymondpineapple

[GLOBAL: userInfoPane.html]
raymondpineapple
  • Enthusiast

  • 33 posts
  • Corona SDK

Thanks for your advice.
As Rob said, the "collectGarbage" has an interval. I just want to know if I can skip such interval when testing.



[TOPIC: post.html]
#20

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 801 posts
  • Corona SDK

Also, as Rob somehow implies, I would not advise to hunt for memory leaks and instead, while developing, just place a memory usage counter on top of everything that monitors constantly, and only tackle issues when a very specific problem arises.

 

The same for releasing texture memory, by the way: in my latest game, instead of cleaning up every imageSheet after use (and then needing to reload it again later on) I find it's easier to just keep them in memory.



[TOPIC: post.html]
#21

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

You can call the garbage collector whenever you want.  Just be aware that it will be a big performance hit on your app.  But if there is a leak (yours or ours), this won't fix it. 

 

Rob



[TOPIC: post.html]
#22

davebollinger

[GLOBAL: userInfoPane.html]
davebollinger
  • Corona Geek

  • 1,205 posts
  • Enterprise

You can call the garbage collector whenever you want.  Just be aware that it will be a big performance hit on your app.  But if there is a leak (yours or ours), this won't fix it. 

 

Rob

 

just to further clarify:  as of Lua 5.1 (which is what Corona currently uses) overall garbage collection performance is proportional to memory usage (specifically, the number of reference-counted objects, rather than their total size), and the gc is constantly running incrementally, so unless you're currently using lots of discrete memory instances, and/or have recently done a lot of thrashing, the performance hit of calling for a full collect might/not be significant.

 

[also note that if you have need to call the gc repeatedly, you can explicitly stop the internal incrementals, and step it yourself with size and frequency of your own choosing.  (advanced topic, and not for the faint of heart, but a potential performance gain in some very specific instances - just be sure to restart it later when done stepping it yourself!)]



[TOPIC: post.html]
#23

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 24,743 posts
  • Corona Staff

This should be fixed in the next daily build - 2015.2597 or later.

 

Rob




[topic_controls]
[/topic_controls]