Android Question Memory leak during screen rotation

mevial

Member
Licensed User
Longtime User
Good day.
I’ve encountered a memory leak that I can’t seem to resolve. In my application, there are several tabs—some contain a ScrollView with several hundred Labels, while others display a graph using AcceleratedSurface. The issue is that when I rotate the device, everything gets redrawn, and after a few dozen rotations, the application crashes. The app is quite large, so I created a simplified version with an increased number of Labels. In this simplified version, the problem occurs after just a few rotations, but the error is exactly the same. If I comment out either the ScrollView or the AcceleratedSurface separately, the issue disappears. How can I eliminate this memory leak?

Main:
#Region  Project Attributes
    #ApplicationLabel: B4A Example
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
    #BridgeLogger: True
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

Sub Process_Globals
End Sub

Sub Globals
'    Dim GPanel As GraphPanelClass
    Dim PowerChartAS As AcceleratedSurface
    Dim panel1 As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
    panel1.Initialize("")
    Activity.AddView(panel1,0,0,100%x,50%y)
    PowerChartAS.Initialize("PCAS",True)
    panel1.AddView(PowerChartAS,0,0,panel1.Width,panel1.Height)
    PowerChartAS.StartRegularDraw(100)


    Log("Initializing Labels")
    Dim sv As ScrollView
    sv.Initialize(100)
    Activity.AddView(sv,0,50%y,100%x,100%y)
    Dim PosY As Int=0
    Dim lbl As Label
    For i=0 To 10000
        lbl.Initialize("")
        lbl.Text=i
        sv.Panel.AddView(lbl,10dip,PosY,100%x,40dip)
        PosY=PosY+40dip
    Next
    sv.Panel.Height=PosY
    
    MemoryUsage
End Sub

Sub PCAS_draw(ac As AS_Canvas)
    For i=1 To 5
        ac.DrawCircle(Rnd(0,panel1.Width),Rnd(0,panel1.Height),5dip,Colors.Green,True,0,True)
    Next
End Sub

Public  Sub MemoryUsage As String
    Dim r            As Reflector
    Dim Text        As String
        
    Dim MaxMemory    As Long
    Dim FreeMemory   As Long
    Dim TotalMemory As Long
    Dim UsedMemory   As Long
    Dim TotalFree   As Long
        
        
    r.Target    = r.RunStaticMethod("java.lang.Runtime", "getRuntime", Null, Null)
        
    MaxMemory      = r.RunMethod("maxMemory")
    FreeMemory    = r.RunMethod("freeMemory")
    TotalMemory   = r.RunMethod("totalMemory")
    UsedMemory   = TotalMemory - FreeMemory
    TotalFree   = MaxMemory   - UsedMemory
        
    Text = "  Max Memory = " &NumberFormat2((MaxMemory/(1024*1024)), 1, 2, 2, True) &" MB"                        _
                     &CRLF &" Total Free        = " &NumberFormat2((TotalFree/(1024*1024)), 1, 2, 2, True) &" MB"       _
                   &CRLF &" Total Memory = " &NumberFormat2((TotalMemory/(1024*1024)), 1, 2, 2, True) &" MB"            _                 
                   &CRLF &" Used Memory = " &NumberFormat2((UsedMemory/(1024*1024)), 1, 2, 2, True) &" MB"               _
                   &CRLF &"  Free Memory = " &NumberFormat2((FreeMemory/(1024*1024)), 1, 2, 2, True) &" MB"         
                
    Log(DateTime.Now&"   Max Memory = " & NumberFormat2((MaxMemory   / (1024*1024)), 1, 2, 2, True) & " MB")
    Log(DateTime.Now&" Total Free   = " & NumberFormat2((UsedMemory      / (1024*1024)), 1, 2, 2, True) & " MB")
    Log(DateTime.Now&" Total Memory = " & NumberFormat2((TotalMemory    / (1024*1024)), 1, 2, 2, True) & " MB")
    Log(DateTime.Now&"  Used Memory = " & NumberFormat2((UsedMemory      / (1024*1024)), 1, 2, 2, True) & " MB")
    Log(DateTime.Now&"  Free Memory = " & NumberFormat2((FreeMemory      / (1024*1024)), 1, 2, 2, True) & " MB")
        
    Return Text
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create (first time) **
Initializing Labels
1742061045698 Max Memory = 192.00 MB
1742061045698 Total Free = 37.09 MB
1742061045698 Total Memory = 50.30 MB
1742061045699 Used Memory = 37.09 MB
1742061045699 Free Memory = 13.21 MB
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create **
Initializing Labels
1742061054840 Max Memory = 192.00 MB
1742061054840 Total Free = 69.98 MB
1742061054841 Total Memory = 76.59 MB
1742061054841 Used Memory = 69.98 MB
1742061054841 Free Memory = 6.60 MB
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create **
Initializing Labels
1742061062908 Max Memory = 192.00 MB
1742061062908 Total Free = 96.42 MB
1742061062908 Total Memory = 96.42 MB
1742061062909 Used Memory = 96.42 MB
1742061062909 Free Memory = 0.00 MB
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create **
Initializing Labels
1742061069786 Max Memory = 192.00 MB
1742061069787 Total Free = 126.51 MB
1742061069787 Total Memory = 128.66 MB
1742061069787 Used Memory = 126.51 MB
1742061069787 Free Memory = 2.16 MB
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create **
Initializing Labels
1742061076702 Max Memory = 192.00 MB
1742061076703 Total Free = 158.91 MB
1742061076703 Total Memory = 158.91 MB
1742061076703 Used Memory = 158.91 MB
1742061076703 Free Memory = 0.00 MB
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Create **
Initializing Labels
art/runtime/indirect_reference_table.cc:128] JNI ERROR (app bug): weak global reference table overflow (max=51200)
art/runtime/indirect_reference_table.cc:128] weak global reference table dump:
art/runtime/indirect_reference_table.cc:128] Last 10 entries (of 51200):
art/runtime/indirect_reference_table.cc:128] 51199: 0x1ce25838 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51198: 0x1ce25700 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51197: 0x1ce255c8 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51196: 0x1ce25490 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51195: 0x1ce25358 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51194: 0x1ce25220 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51193: 0x1ce250e8 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51192: 0x1ce1cf40 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51191: 0x1ce1ce08 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] 51190: 0x1ce1ccd0 android.view.RenderNode
art/runtime/indirect_reference_table.cc:128] Summary:
art/runtime/indirect_reference_table.cc:128] 3 of byte[] (2016 elements) (3 unique instances)
art/runtime/indirect_reference_table.cc:128] 3 of byte[] (6864 elements) (3 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (9408 elements)
art/runtime/indirect_reference_table.cc:128] 5 of byte[] (12544 elements) (5 unique instances)
art/runtime/indirect_reference_table.cc:128] 4 of byte[] (13720 elements) (4 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (20160 elements)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (23520 elements)
art/runtime/indirect_reference_table.cc:128] 9 of byte[] (28224 elements) (9 unique instances)
art/runtime/indirect_reference_table.cc:128] 27 of byte[] (36100 elements) (27 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (43904 elements)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (44044 elements)
art/runtime/indirect_reference_table.cc:128] 2 of byte[] (47432 elements) (2 unique instances)
art/runtime/indirect_reference_table.cc:128] 2 of byte[] (48400 elements) (2 unique instances)
art/runtime/indirect_reference_table.cc:128] 17 of byte[] (50176 elements) (17 unique instances)
art/runtime/indirect_reference_table.cc:128] 3 of byte[] (63504 elements) (3 unique instances)
art/runtime/indirect_reference_table.cc:128] 2 of byte[] (64512 elements) (2 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (102400 elements)
art/runtime/indirect_reference_table.cc:128] 3 of byte[] (108192 elements) (3 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (112112 elements)
art/runtime/indirect_reference_table.cc:128] 3 of byte[] (112896 elements) (3 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (147456 elements)
art/runtime/indirect_reference_table.cc:128] 4 of byte[] (150096 elements) (4 unique instances)
art/runtime/indirect_reference_table.cc:128] 5 of byte[] (367236 elements) (5 unique instances)
art/runtime/indirect_reference_table.cc:128] 1 of byte[] (1048576 elements)
art/runtime/indirect_reference_table.cc:128] 31 of java.lang.DexCache (31 unique instances)
art/runtime/indirect_reference_table.cc:128] 2 of dalvik.system.PathClassLoader (1 unique instances)
art/runtime/indirect_reference_table.cc:128] 51066 of android.view.RenderNode (51066 unique instances)
art/runtime/indirect_reference_table.cc:128]
 

peacemaker

Expert
Licensed User
Longtime User
If you use a single SV or other container - i guess, it must be publicly\static declared (Globals).
And all dynamically created views - must be unloaded at Activity_Pause (during orientation change): sv.panel.RemoveAllViews.
And creating the labels is needed to do in Activity_Resume.
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
Longtime User
Do you need both landscape and portrait views?

How many of your users are going to arc up if you fix it to one or the other? You might even be doing them a favour - I have a couple of apps that annoyingly swap orientations when they shouldn't. Calculator is one, camera is another (eg when taking top-down photos of a book).
 
Upvote 0

mevial

Member
Licensed User
Longtime User
Do you need both landscape and portrait views?

How many of your users are going to arc up if you fix it to one or the other? You might even be doing them a favour - I have a couple of apps that annoyingly swap orientations when they shouldn't. Calculator is one, camera is another (eg when taking top-down photos of a book).
Yes, I need both. 50/50.
 
Upvote 0

mevial

Member
Licensed User
Longtime User
Can you reproduce it without AcceleratedSurface?
What is the difference between RemoveAllViews and RemoveView? In my application, I tried collecting pointers to all the Views and then calling RemoveView for each of them in Activity_Create. This reduced the scale of the leak but didn’t completely eliminate it.
 
Upvote 0

peacemaker

Expert
Licensed User
Longtime User
RemoveAllViews and RemoveView? In my application, I tried collecting pointers to all the Views
That's it's removing the pointers to these created objects. Childs, or the container view itself.
IMHO, the container views should be declared once, loaded into the layout, but all other views inside the containers must be created\loaded\linked to\clicked to... and removed (with the pointers list of them) when the activity is being recreated (rotation).
 
Upvote 0

mevial

Member
Licensed User
Longtime User
That's it's removing the pointers to these created objects. Childs, or the container view itself.
IMHO, the container views should be declared once, loaded into the layout, but all other views inside the containers must be created\loaded\linked to\clicked to... and removed (with the pointers list of them) when the activity is being recreated (rotation).
Collecting pointers:
Sub AddToView(Master As Panel,Slave As View,Left As Double,Top As Double,Width As Double,Height As Double)
    If Master.IsInitialized And Slave.IsInitialized Then
        Master.AddView(Slave, Left, Top, Width, Height)
        If Main.ViewList.IsInitialized Then
            Main.ViewList.InsertAt(0,Slave)
        Else
            Log(DateTime.Now&" ViewList is not initialized")
        End If
    Else
        If Not(Master.IsInitialized) Then
            Log(DateTime.Now&" Master not found")
        Else
            Log(DateTime.Now&" Slave not found")
        End If
    End If
End Sub

Activity_Create:
        If ViewList.IsInitialized Then
            i=0
            For Each v As View In ViewList
                If v.IsInitialized Then
                    v.RemoveView
                    i=i+1
                End If
            Next
            ViewList.Clear
            Log(DateTime.Now&" removing all "&i&" views in main")
        End If
 
Upvote 0

peacemaker

Expert
Licensed User
Longtime User
B4X:
        If ViewList.IsInitialized Then
            Master.RemoveAllViews
            ViewList.Clear
        End If

What is "Dim sv As ScrollView" ? Why is it not in Globals ?
 
Upvote 0

mevial

Member
Licensed User
Longtime User
B4X:
        If ViewList.IsInitialized Then
            Master.RemoveAllViews
            ViewList.Clear
        End If

What is "Dim sv As ScrollView" ? Why is it not in Globals ?
I moved this to globals, didn't helps in my case. Helps RemoveAllViews in the Activity_Pause, also just StopRegularDraw works perfectly. In the my real app each tab in tabhost - is a dedicated class.
 
Upvote 0
Top