Does "Sprite.dispose" also disposes the Sprite.bitmap?
I believe that I asked this same question a long time ago but I forgot and I need to be sure. My guess would be "no" because sometimes you assign a Cached bitmap to the sprite and obviously you don't want that one to be disposed.
EDIT:
Wouldn't mind a FAQ/sticky with questions like these. They are very important and people like me tend to forget them + new people need to know it as well.
Sprite.dispose also disposes bitmap?
● ARCHIVED · READ-ONLY
-
-
well when you say Sprite.dispose you only dispose the global you use for this sprite.new!
but sprite.bitmap.dipose dispose the specific file you use in the cache method -
So if I have
bmp = Bitmap.new(1, 1)sprite = Sprite.newsprite.bitmap = bmpsprite.dispose#< Memory leak here?>then I have a memory leak right? And I should use:
bmp = Bitmap.new(1, 1)sprite = Sprite.newsprite.bitmap = bmpsprite.bitmap.dispose; sprite.dispose # I'll probably extend the Sprite class to dispose both...Is that correct? -
hooo I don't use the same of you for doing this like I do in this way :
def something@sprite = Sprite.new@sprite.bitmap = bmp (if something else it is use)bmp = Cache.something("file")enddef dispose@sprite.dispose@sprite.bitmap.disposeendyour way how you use is good but I advise you to separate your "create" method and your "dispose" method for avoid lag's
I advise you to check moghunter title screen (the new not the old) script it show a good method for show how to use sprite's in script
but just for ask why did you put your both dispose in the same ligne (I never do that and I notice this in a lot of your script so I just wounder)
and I wounder why did you use Bitmap and sprite in same time is not necessary Bitmap it use for Font thing's not picture Sprite all the job and I think if you call bitmap and sprite in same time your script will crash -
Because it was a sample. Can you point me out what script of mine does that?your way how you use is good but I advise you to separate your "create" method and your "dispose" method for avoid lag's
..
but just for ask why did you put your both dispose in the same ligne (I never do that and I notice this in a lot of your script so I just wounder)
I didn't use them at the same time. I put a semicolon between it. Those are actually 2 lines.and I wounder why did you use Bitmap and sprite in same time is not necessary Bitmap it use for Font thing's not picture Sprite all the job and I think if you call bitmap and sprite in same time your script will crash
So anyway, when testing:
a = Bitmap.new(1, 1)sprite = Sprite.newsprite.bitmap = asprite.disposeputs sprite.bitmap.nil?# <<< falseputs sprite.bitmap.disposed? # <<< falseI still assume that I have to dispose both manually.
class Sprite; def dispose_all self.bitmap.dispose disposeend; enda = Bitmap.new(1, 1)sprite = Sprite.newsprite.bitmap = asprite.dispose_allputs sprite.bitmap.nil? # <<< falseputs sprite.bitmap.disposed? # <<< trueAs an extra note just in case:
I can not use the Cache (unless I clone it) because I use destructive methods like clear() and blt() and other nasty stuff on that bitmap and I don't want to alter the original because other scripts may use it as well. -
hum if i remember in your napoelon map you use this technic not sure because I don't have the master demo open front of me
and also I understand now for using bitmap and sprite just in begin this don't give
and don't use cache? ho destructive method I never do that so for me something I never use so it is pretty new
but your structure seem good but like I said I don't use the same method of your's I just wounder why did you put the bitmap it is for a font or something else? -
I wouldn't mind an faq/thread on memory-related things that people should be aware of.
-
I think you'd have to dispose of the bitmap first, and then the sprite. If you dispose of the sprite first, you've lost the link to the bitmap.
-
As far as I know, if you lost the link to the bitmap, it disposes automatically
So, unless you use Cache.something(filename), the answer would be "yes"
Edit :
I just read some of the replies. It seems you should disposed it manually.
But, it doesn't matter if you don't dispose them I think. For example, you put the sprite object to @sprite
and then you dispose the @sprite and make them nil
@sprite.dispose@sprite = nilYou lost the link to the bitmap. And it will automatically disposed -
If you lose all references to the Bitmap, it will be Garbage Collected, which is not the same thing as disposing it. There is a, albeit very slight, difference in the amount of memory used by Bitmaps that are disposed and then GC'd as opposed if you just let the GC collect them. Just letting the GC get them has not, as of yet, shown to cause any crashes or instability in the program, though (like not disposing sprites that are associated with a disposed Viewport does). Also, creating and disposing a bitmap and discarding the reference will leave you with very slightly more memory usage than before you created the bitmap, so the function (or GC) is leaky either way, although it is so minor (eg., measured in bytes) that it would never feasibly make an impact during even the longest run of an RM project. If nothing else, it is good practice to dispose of any bitmap you no longer need, which is basically just any bitmap you have drawn in or altered (excluding window contents where this happens automatically). You do not need to dispose Cached bitmaps, for the most part, unless you are using an excessive amount of memory, for example, if the process is using over 500 mb of the hard ~1.8gb limit. Animations, battlers, and backgrounds are both cached and disposed when they are no longer needed by default, which is usually more than enough to keep mem usage within reason.
-
I tried disposing the sprite first but I still have the link to the bitmap. Because dispose does not equal nil. I still would dispose the bitmap first because it makes more sense.I think you'd have to dispose of the bitmap first, and then the sprite. If you dispose of the sprite first, you've lost the link to the bitmap.
@sprite = Sprite.new
@sprite.bitmap = Bitmap.new(1,1)
@sprite.dispose
puts @sprite.bitmap.disposed? # <<< returns false and there is still a reference.
@Theo:
I do remember that setting a value to nil is not necessary in RGSS. Only dispose is.
@Mithran:
Yes the Cache is safe to use.
The code-blocks below causes a crash. Remove the 1000000.times and it works.
So I made it more realistic and changed the bitmap size to 512x512 and the .times to 4000 instead.Spoilerclass Mem_test def initialize @bitmap = Bitmap.new(1000, 1000) endend1000000.times do @a = Mem_test.new @a = nilendThis variation crashes just like the one from above:
class Mem_test def initialize @bitmap = Bitmap.new(512, 512) @sprite = Sprite.new @sprite.bitmap = @bitmap @sprite.dispose endend4000.times do @a = Mem_test.new @a = nilend
My conclusion is that bitmaps MUST be disposed. An 1x1 bitmap does nothing. But a larger bitmap starts making an impact on the system and it won't crash right away but it will after an hour or so.
So there is a memory limit for RPG Maker... Because it crashes as soon as my memory reaches 1.899.708k (+0.5MB is > 1.9GB so that's why the number I guess) but my memory usage total is only about ~25% according to the task manager. I don't see why RPG Maker can't use all available RAM?
Crap now I have to check all my scripts...
Update:
Maybe I was wrong... The Garbage Collector just isn't smart enough to collect in time for my test code.
New code (no crash):
My 2nd conclusion:Spoilerclass Mem_test def initialize @bitmap = Bitmap.new(512, 512) @sprite = Sprite.new @sprite.bitmap = @bitmap @sprite.dispose endend10000.times do |i| @a = Mem_test.new @a = nil GC.start if i % 500 == 0end
So my 2nd conclusion is that disposing the bitmap is not required as long as the GC collects before you reach the 1.9GB RAM limit.
So if you work with big or with lots of bitmaps, then you have a few options:
- Option 1: a basic understanding of the GC is required (Yeah you can hope that the GC is smart enough to collect before that happens and usually it will, but not always.)
- Option 2: you have to dispose the bitmaps manually
- Option3: make sure that you always stay within the 1.9GB RAM limit & set your minimum game RAM requirements to 1.9GB.
- Option4: use a custom Cache (see post below)
I personally prefer option #2 or #4.
/off-topic
I did notice that the Cache indeed disposes it's bitmaps when the default code no longer needs it. This was another reason for me to use non-cached bitmaps because some of my custom scripts crashed because they were using cached bitmaps that were disposed (but the reference was still there). The code that calls Cache.clear is probably internal so I can't see what it does nor alter it. -
I was thinking about creating a new Cache class that contains bitmaps used for destructive purposes and such and they are stored in a hash by category. They can be disposed per category.
Sample script (no crash):
This way I will never overlook a bitmap.Spoiler# Never forget an undisposed bitmap ever again# Create new bitmaps with Cache.new_bmp:)your_category, width, height)# When your script/scene finishes call: Cache.dispose:)your_category)# Author: Napoleon# Version: 0.01module Cache def destructive_cache; @@destructive_cache; end def destructive_cache=value; @@destructive_cache = value; end @@destructive_cache = Hash.new{|h, k| h[k] = []}; def self.new_bmp(category, width, height) bmp = Bitmap.new(width, height) @@destructive_cache[category] << bmp bmp end # This method isn't really necesary... def dispose_bmp(category, bmp) return if !@@destructive_cache.has_key(category) deleted = @@destructive_cache[category].delete(bmp) deleted.dispose if deleted return deleted end def self.dispose(category) return if !@@destructive_cache.has_key?(category) @@destructive_cache[category].each { |bmp| bmp.dispose } return @@destructive_cache.delete(category) endend#===============================================================================# Test Code#===============================================================================class Mem_test def initialize @bitmap = Cache.new_bmp:)mem_test, 512, 512) @sprite = Sprite.new @sprite.bitmap = @bitmap @sprite.dispose endend10000.times do |i| @a = Mem_test.new @a = nil Cache.dispose:)mem_test) if i % 500 == 0end
Update:
And then I noticed that I can just call "GC.start" when a scene terminates or after a big operation on bitmaps... -
You have a hard memory limit because the application has a hard memory limit:
See here:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa366778(v=vs.85).aspx
On top of that, only a portion of that can be allocated to the Ruby environment. This is where the limit comes from. Ruby also has a processor thread lock (limited to one processor thread per process), so you aren't going to see any performance improvements on multi-core processors and why you can start getting framerate issues even when the process is using < 10% total processor.
Yes, GC is too slow to keep up if you are constantly creating and destroying bitmaps, but explicitly telling GC to start will cause a performance hit. GC.start is also more of a suggestion to Ruby than a command, it just prioritizes the operation.
Remember that you have ~1.8 gb to play around with before you are approaching a hard limit. 500mb is within reason to keep the program running at. If you are disposing just for memory usage, remember that the processor cost of loading, disposal, and GC of a bitmap is very high, whereas keeping it in memory has almost no processor cost. You are kind of creating a bottleneck where none existed before. If the numbers were high enough where the operation was needed, you (or someone with a possibly slower computer) would likely take a performance hit. It would be mostly hidden if done during a transition, though. If cache is used properly and you just dispose stuff you draw in, you should already be well within these numbers without having to resort to mass disposal.
Cache.clear is never invoked in the base code, it is simply there as a way to clear the cache. Graphics.__reset directly disposes all sprites and bitmaps in memory without clearing the cache variables, but Cache has logic to reload any bitmaps present in the hash if they have been disposed.
While that approach is fine, you still have to explicitly call a method to dispose them (this time directly to the Cache, and by category) when the scene switches over. You could simply have Sprite call a try_dispose on destruction which would be set up to test if dispose is legal on the sprite (anything created through a specific method would have the destructible? flag set), and if so, disposes it right there. You could also just subclass a Sprite to do it for you and use that kind of sprite. If you miss a Sprite, though, you are still in trouble, as this is much worse of an issue than losing a few bytes to bitmap GC....snip
This way I will never overlook a bitmap.
Otherwise, I think I am "for" having to manually dispose bitmaps in this case. Having it be 'automatic' leads to practices of not having to explicitly call dispose when dealing with these objects, which is not what you want if they can cause instability. This doesn't happen with bitmaps because something is VERY wrong if you are approaching the memory or GDI object limit, but the addition of the dispose_all_windows method in Scene_Base which has the kind of 'automatic' handling has led to some scripters missing dispose on some windows that weren't specifically stored in the scene's instance variables (say, if they store them in an array or in as a instance variable of of a window) that can lead to Game.exe crashes if not disposed. -
Oh so the limit comes from Windows. That is good to know. It was then probably the first time in my life for reaching that limit with one of my own creations hehe. Only happened through 'stress-testing' but still. (This info would also qualify as good FAQ material imo)
I'm aware that the cost of disposing a bitmap is relatively high. That's why I usually only perform such actions on just a few smaller bitmaps or during a scene-switch. A small 'hiccup' is then acceptable to me.
Yes I'm also aware that missing a sprite is a real problem. Missing a bitmap is no problem at all as long as there aren't too many within a short time interval (like in an update-loop) so that the GC can collect them before they do become a problem. And the chances that your bitmaps will reach the ~1.9 GB limit before the GC catches them are extremely slim if you coded it properly, but not impossible.
Mmm a destructible flag is a much better solution than any of those that I came up with. The code below also prevents a crash as long as the destructible-property is set to true.
It works nicely, it's short and should be compatible with any other script :) . You do have to add a very few "sprite.destructible = true" for the problematic occasions but nothing big.Spoilerclass Sprite attr_accessor :destructible alias nap_bmp_destruct_dispose dispose def dispose self.bitmap.dispose if @destructible && self.bitmap nap_bmp_destruct_dispose end
But then again, the above 'mini-script' should not be necessary for 99.999% of the occasions and is more likely to happen during some huge performance test but (almost) never live. -
i agree with theo. you need to set the variable to nil to avoid already disposed object error in "some" case.
@sprite.bitmap.dispose
@sprite.dispose
@sprite = nil #<<<<< this one.
it occur with lots of scripts i wrote. i don't have base to explain it though.
i just think the scene/class where i wrote the code still registering the @sprite and try to update it.
try adding "puts @sprite" and it still registered as Sprite instance object...
so it throw already disposed object error. i didn't have screenshot for it. but it happen many times before. -
After I call sprite.dispose I fully expect the sprite to no longer be updated. It would be weird (or even a bug in the default RGSS) if it would. But I have not tested whether that is the case or not because that isn't my problem. My problem was just that the sprite did not dispose the bitmap as wel (which we solved).
I do not call the @sprite.dispose in the middle of that same @sprite.update() method. In such case I would understand a disposed error.
Also setting the sprite to nil should not be necessary. It may or may not trigger the GarbageCollector to collect it faster but I see no real reason for this unless you create and dispose hundreds of sprites per frame (which is bad coding anyway, re-use it instead I'd say). -
Just to clear things up:
Sprites need to be explicitly updated, there is nothing "hidden" that nulling a variable will do in this regard. Personally, I nil the variable whenever I need to create or dispose sprites on-the-fly, so I can easily determine whether or not the sprite still exists and needs to be handled ("if @sprite" versus "if @sprite && !@sprite.disposed?"). Nulling the variable in this case is just another form of flow control. Removing the reference also removes the possibility for you to even attempt to access the metadata methods from the base hidden class (the topmost update, x, y, ox, oy, src_rect, etc.) that cause the RGSS Error - disposed sprite to be thrown; instead, you'll get an undefined method for nilclass error; and that is only if you didn't already have flow control in place to consider if the sprite variable was nil. If you were to call dispose locally, within the instance, you'd just have to make sure not to hit any of the metadata references on your way out of the scope - which I would imagine would be difficult if you were in the update method.
You won't ever throw the disposed sprite error just from the sprite sitting in a variable untouched, disposed or otherwise, and the object will get GC'd once the last variable reference is removed, anyway (if it was in an instance variable of an instance of a Spriteset in an instance of a scene Scene, when the scene is no longer stored in any variable, then the entire tree will be marked for GC). Nulling the variable holding a Sprite just to attempt to trigger an early GC both impractical and pointless as the memory footprint for Sprites is very small and the important bits are freed by "dispose".
Graphics does not even care a the Sprite is actually referenceable via a variable or not to consider it for display, although those that do not have a reference will be GC'd eventually, whether or not you have called dispose. Ruby GC won't trigger on an object if it has any reference path (excluding ObjectSpace), including a local variable within a closure stored in some variable somewhere. After the Sprite has been GC'd, the sprite will disappear from the screen (stop being considered by Graphics), even if it has not been disposed. However, the destructor used by the GC is not complete for the metadata and, at best, leaves a small amount more memory behind than collecting a disposed sprite would. At worst (in the case of being associated with a Viewport), it will cause an instability leading to a Game.exe crash.
Similarly, losing the reference to a bitmap will[\i] cause it to be garbage collected, but there is a small difference in memory between what GC leaves behind on bitmaps that were disposed versus those that weren't. Although this has not been shown to lead to any crashes, and having bitmaps GC'd does also appear to remove them from counting toward the GDI limit, it is still good practice to manually dispose bitmaps you no longer need (see: the actual subject of this thread). I still think explicit control beats an implicit disposal here, but having to set a flag ahead of time is a decent compromise. -
mySprite.destroy(true);
from PixiJS API Documentation
destroy (options) overrides
Sprite.js:419
Destroys this sprite and optionally its texture and children
Name - Type - Description
options - object | boolean - [optional] Options parameter. A boolean will act as if all options have been set to that value
options:
Name - Type - Default - Description
children - boolean - false - [optional] if set to true, all the children will have their destroy method called as well. 'options' will be passed on to those calls.
texture - boolean - false - [optiona] Should it destroy the current texture of the sprite as well
baseTexture - boolean - false - [optiona] Should it destroy the base texture of the sprite as well -
idk why u reply JS explanation in Ruby threadmySprite.destroy(true);
from PixiJS API Documentation
destroy (options) overrides
Sprite.js:419
Destroys this sprite and optionally its texture and children
Name Type Description
options object | boolean [optional] Options parameter. A boolean will act as if all options
have been set to that value
options:
Name Type Default Description
children boolean false [optional] if set to true, all the children will have their
destroy method called as well.
'options' will be passed on to those calls.
texture boolean false [optiona] Should it destroy the current texture of the sprite as well
baseTexture boolean false [optiona] Should it destroy the base texture of the sprite as well -
[necro]trapless [/necro]
This thread is almost 4 years old and for another engine.