Android Question Bitmaps, subs and garbage collection

SteveTerrell

Active Member
Licensed User
Longtime User
Creating (and "deleting") too many bitmaps seems to be a bit of a problem if the garbage collector is not running very often compared with the bitmap creation rate.

As an example - GaussianBlurring a bitmap as a slider specifying the blur radius is moved.

I can see two ways of coding this.

case 1:

Sub work(sourceBm As Bitmap) As Bitmap
Dim localReturnBm As Bitmap
'do something with sourceBm and put it in localReturnBm
Return localReturnBm
End Sub

and called as :-

Dim bitmapToDisplay As Bitmap
bitmapToDisplay= work(originalBitmap)



case 2:
Sub work(sourceBm As Bitmap, returnBm As Bitmap)
'do something with sourceBm and put it in returnBM
End Sub

and called as :-

Dim bitmapToDisplay As Bitmap
work(originalBitmap, bitmapToDisplay)



Do these cases create and/or destroy the same number of bitmaps?

I realise there are ways of slowing down the creation rate in this example but it would be good to have a picture of the inside workings of the sub/return object mechanism.
 

Informatix

Expert
Licensed User
Longtime User
What is your Android version? The garbage collector is a concern only before Honeycomb. Since API 11, it is always run before an OutOfMemory error occurs, so if the error is raised, that means you really lack memory and the garbage collector cannot do anything for you.
 
Upvote 0

SteveTerrell

Active Member
Licensed User
Longtime User
What is your Android version? The garbage collector is a concern only before Honeycomb. Since API 11, it is always run before an OutOfMemory error occurs, so if the error is raised, that means you really lack memory and the garbage collector cannot do anything for you.

Google Nexus 7 with KitKat (4.4)

The above question is of interest anyway if only to get a better understanding of what is under the B4A hood.

I must admit I am assuming it is a memory issue. Whatever the cause I get a silent death (debugger drops out and app stops running) generally when there are a sequence of bitmaps being created from a slider action. There are no error reports or logs entries relating to the crash, filtered or otherwise. The bitmaps are taking 10-50ms to create. If the app does not die I will see GC report around 300K recovered perhaps after a few minutes. The bitmaps are about 100*100
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Google Nexus 7 with KitKat (4.4)

The above question is of interest anyway if only to get a better understanding of what is under the B4A hood.

I must admit I am assuming it is a memory issue. Whatever the cause I get a silent death (debugger drops out and app stops running) generally when there are a sequence of bitmaps being created from a slider action. There are no error reports or logs entries relating to the crash, filtered or otherwise. The bitmaps are taking 10-50ms to create. If the app does not die I will see GC report around 300K recovered perhaps after a few minutes. The bitmaps are about 100*100
That looks like an OOM indeed. As I don't know exactly what you do with your bitmaps, I cannot give you an advice, except creating a pool: instead of allocating new bitmaps, why don't you reuse the existing ones with a new content?
 
Upvote 0

SteveTerrell

Active Member
Licensed User
Longtime User
That looks like an OOM indeed. As I don't know exactly what you do with your bitmaps, I cannot give you an advice, except creating a pool: instead of allocating new bitmaps, why don't you reuse the existing ones with a new content?
The blurred bitmap is created from the original by android.renderscript.ScriptIntrinsicBlur and is finally copied back into B4A by

B4X:
args(0) =  outputBitmap
    types(0) = "android.graphics.Bitmap"
    tmpOut.RunMethod4("copyTo",args, types)

I got this really helpful code from DrewG here

I am hoping that the case 2 approach will al least reduce the number of bitmaps being created (compared with case 1)

P.S. Thanks for your interest and help.
 
Upvote 0

DrewG

Member
Licensed User
Longtime User
I had the same problem with my code, specifically I am blurring 256x256 bitmaps, after exactly 112 blur passes process ran OOM.

The problem is because the "rs" reflector never seems to be garbage collected, it sticks around and runs the process OOM even if it is contained in a closed block.

The solution was to make the reflector a global variable and only assign it once by running InitGB() in Activity_Create:
B4X:
'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim rs As Reflector
End Sub

Public Sub InitGB()
    Dim Obj1 As Reflector
    Dim args(1) As Object
    Dim types(1) As String
    'RenderScript rs = RenderScript.create(getApplicationContext());
    args(0) =  Obj1.GetContext
    types(0) = "android.content.Context"
    rs.Target = Obj1.RunStaticMethod("android.renderscript.RenderScript","create", args, types)
End Sub

Public Sub GaussianBlur(bm As Bitmap, radius As Float) As Bitmap
   
    Dim Obj1 As Reflector
    Dim time As Long
    time = DateTime.Now
    Dim args(1) As Object
    Dim types(1) As String
   
    'Element.U8_4(rs)
    Dim oU8 As Reflector
    args(0) = rs.Target 
    types(0) = "android.renderscript.RenderScript"
    oU8.Target = Obj1.RunStaticMethod("android.renderscript.Element","U8_4", args, types)
   
    '    ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur
    '.create(rs, Element.U8_4(rs));
    Dim theIntrinsic As Reflector
    Dim args2(2) As Object
    Dim types2(2) As String
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  oU8.Target 
    types2(1) = "android.renderscript.Element"
    theIntrinsic.Target = Obj1.RunStaticMethod("android.renderscript.ScriptIntrinsicBlur","create", args2, types2)
   
    'Allocation tmpIn = Allocation.createFromBitmap(rs, bm);
    'Dim bm As Bitmap
    'bm = LoadBitmap(File.DirAssets, "reusedefault.bmp")
    Dim tmpIn As Reflector
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  bm 
    types2(1) = "android.graphics.Bitmap"
    tmpIn.Target = Obj1.RunStaticMethod("android.renderscript.Allocation", "createFromBitmap", args2, types2)

    'Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
    Dim outputBitmap As Bitmap
    outputBitmap.InitializeMutable(bm.Width,bm.Height)
    Dim tmpOut As Reflector
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  outputBitmap 
    types2(1) = "android.graphics.Bitmap"
    tmpOut.Target = Obj1.RunStaticMethod("android.renderscript.Allocation", "createFromBitmap", args2, types2)
   
    'theIntrinsic.setRadius(25.f);
    args(0) =  radius
    types(0) = "java.lang.float"
    theIntrinsic.RunPublicmethod("setRadius",args,types)

    'theIntrinsic.setInput(tmpIn);
    args(0) =  tmpIn.Target
    types(0) = "android.renderscript.Allocation"
    theIntrinsic.RunPublicmethod("setInput",args,types)

    'theIntrinsic.forEach(tmpOut);
    args(0) =  tmpOut.Target
    types(0) = "android.renderscript.Allocation"
    theIntrinsic.RunPublicmethod("forEach",args,types)
   
    'tmpOut.copyTo(outputBitmap);   
    args(0) =  outputBitmap
    types(0) = "android.graphics.Bitmap"
    tmpOut.RunMethod4("copyTo",args, types)
   
    Log("Blur took: " & (DateTime.Now - time) & " ms")

    Return outputBitmap
    'ImageView1.Bitmap = outputBitmap
   
End Sub
 
Upvote 0

SteveTerrell

Active Member
Licensed User
Longtime User
I had the same problem with my code, specifically I am blurring 256x256 bitmaps, after exactly 112 blur passes process ran OOM.

The problem is because the "rs" reflector never seems to be garbage collected, it sticks around and runs the process OOM even if it is contained in a closed block.

The solution was to make the reflector a global variable and only assign it once by running InitGB() in Activity_Create:
B4X:
'Code module
'Subs in this code module will be accessible from all modules.
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Dim rs As Reflector
End Sub

Public Sub InitGB()
    Dim Obj1 As Reflector
    Dim args(1) As Object
    Dim types(1) As String
    'RenderScript rs = RenderScript.create(getApplicationContext());
    args(0) =  Obj1.GetContext
    types(0) = "android.content.Context"
    rs.Target = Obj1.RunStaticMethod("android.renderscript.RenderScript","create", args, types)
End Sub

Public Sub GaussianBlur(bm As Bitmap, radius As Float) As Bitmap
  
    Dim Obj1 As Reflector
    Dim time As Long
    time = DateTime.Now
    Dim args(1) As Object
    Dim types(1) As String
  
    'Element.U8_4(rs)
    Dim oU8 As Reflector
    args(0) = rs.Target
    types(0) = "android.renderscript.RenderScript"
    oU8.Target = Obj1.RunStaticMethod("android.renderscript.Element","U8_4", args, types)
  
    '    ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur
    '.create(rs, Element.U8_4(rs));
    Dim theIntrinsic As Reflector
    Dim args2(2) As Object
    Dim types2(2) As String
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  oU8.Target
    types2(1) = "android.renderscript.Element"
    theIntrinsic.Target = Obj1.RunStaticMethod("android.renderscript.ScriptIntrinsicBlur","create", args2, types2)
  
    'Allocation tmpIn = Allocation.createFromBitmap(rs, bm);
    'Dim bm As Bitmap
    'bm = LoadBitmap(File.DirAssets, "reusedefault.bmp")
    Dim tmpIn As Reflector
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  bm
    types2(1) = "android.graphics.Bitmap"
    tmpIn.Target = Obj1.RunStaticMethod("android.renderscript.Allocation", "createFromBitmap", args2, types2)

    'Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
    Dim outputBitmap As Bitmap
    outputBitmap.InitializeMutable(bm.Width,bm.Height)
    Dim tmpOut As Reflector
    args2(0) =  rs.Target
    types2(0) = "android.renderscript.RenderScript"
    args2(1) =  outputBitmap
    types2(1) = "android.graphics.Bitmap"
    tmpOut.Target = Obj1.RunStaticMethod("android.renderscript.Allocation", "createFromBitmap", args2, types2)
  
    'theIntrinsic.setRadius(25.f);
    args(0) =  radius
    types(0) = "java.lang.float"
    theIntrinsic.RunPublicmethod("setRadius",args,types)

    'theIntrinsic.setInput(tmpIn);
    args(0) =  tmpIn.Target
    types(0) = "android.renderscript.Allocation"
    theIntrinsic.RunPublicmethod("setInput",args,types)

    'theIntrinsic.forEach(tmpOut);
    args(0) =  tmpOut.Target
    types(0) = "android.renderscript.Allocation"
    theIntrinsic.RunPublicmethod("forEach",args,types)
  
    'tmpOut.copyTo(outputBitmap);  
    args(0) =  outputBitmap
    types(0) = "android.graphics.Bitmap"
    tmpOut.RunMethod4("copyTo",args, types)
  
    Log("Blur took: " & (DateTime.Now - time) & " ms")

    Return outputBitmap
    'ImageView1.Bitmap = outputBitmap
  
End Sub

This is very interesting, thanks for the work and the information.
Steve
 
Upvote 0

Matti81

New Member
Licensed User
Longtime User
From my experiences:
Creating a renderscript object is quite a costly thing (can take several dozends of milliseconds).
So it´s a good idea to create it once and use it as long as possible. I never needed more than one rs object within an app.

If you need ScriptIntrinsicBlur (or other renderscript scripts) several times (e.g. in real time image processing), declare
the elements, allocations and scripts as global variables too and create them also only once. In the "loop" (e.g. camera preview)
then just execute the predefined script(s).
 
Upvote 0
Top