Flutter iOS crash with EXC_BAD_ACCESS error
A couple thoughts for the permanently increasing memory usage because it is practically impossible to answer your question without having a full example.
-
Firebase caches the objects, thats why it is faster, the 2nd time. This should not effect your memory usage by an amount that breaks the app.
-
I ask this question by suspecting that the stream still occupies the memory even after the user moves to another screen...
Maybe you are using Navigator.push*
and never pop any of the previous
routes, all the lists in the previous routes are being kept in memory.
-
Track the dispose calls, see if the widgets get cleaned up. By using logging, breakpoints or even better track the widget counts by using the DevTools memory view: https://docs.flutter.dev/development/tools/devtools/memory The memory snapshots can tell you exactly where your memory is being used.
-
In general it is better to move the sorting out of the
build
function. But this is more of a performance hint than a problem. -
Your memory problems may be unrelated to the
EXC_BAD_ACCESS
crash but you should try to solve them first. It will make it easier to find the underlying cause, if there is any.
edn
And I am a big Machine Learning enthusiast. I am particularly focusing on Deep Neural Networks, Tensorflow and Python. You are welcome to get in touch for any discussion for closer cooperation.
Updated on January 03, 2023Comments
-
edn over 1 year
I am developing an app with flutter and testing the app on a physical iOS device (Iphone 7). The iOS version is: 15.3.1 And the flutter version is: 2.10.3
When I am testing my app, I occasionally get crashes. The crash gives the error below. It does not always crash at the same place so I do not know what code to share here. The error message itself does not say much to me and I could not find any helpful information on the net wrt this error.
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x443b8000443b8000) frame #0: 0x0000000198405048 libobjc.A.dylib`objc_msgSend + 8 libobjc.A.dylib`objc_msgSend: -> 0x198405048 <+8>: ldr x13, [x0] 0x19840504c <+12>: and x16, x13, #0xffffffff8 0x198405050 <+16>: mov x15, x16 0x198405054 <+20>: ldr x11, [x16, #0x10] Target 0: (Runner) stopped. Lost connection to device. Exited (sigterm)
I am totally clueless of what to check after many tries for several days. Would anyone with more experience in this field be able to help me?
ADDENDUM:
I wonder if I am doing sth wrong with my Stream Builders. Here is a shortened version of my code:
class PrepareList extends StatefulWidget { final String place; const PrepareList({ Key? key, required this.place, }) : super(key: key); @override State<PrepareList> createState() => _PrepareListState(); } class _PrepareListState extends State<PrepareList> { late final Stream? _listStream; @override void initState() { super.initState(); String dbChild = "events/" + widget.place + "/"; final db = FirebaseDatabase.instance .ref() .child(dbChild) .orderByKey() .limitToLast(1500); _listStream = db.onValue; } @override Widget build(BuildContext context) { return StreamBuilder( stream: _listStream, builder: (context, AsyncSnapshot<dynamic> dbEvent) { if (dbEvent.hasError) { return CircularProgressIndicator(); } else if (dbEvent.hasData) { DataSnapshot dataSnapshot = dbEvent.data!.snapshot; List<EventDetails> placeEventList = []; if (dataSnapshot.value != null) { (dataSnapshot.value as Map<dynamic, dynamic>) .forEach((key, value) { placeEventList.add(EventDetails.fromRTDB(value)); }); placeEventList .sort((a, b) => a.dateEST.compareTo(b.dateEST)); return AttendeeList(placeEventList: placeEventList, place: place); } else { return PlaceDataNotAvailable(place: widget.place); } } else { return CircularProgressIndicator(); } }); } } class AttendeeList extends StatefulWidget { final List<EventDetails> placeEventList; final String place; const AttendeeList({ Key? key, required this.placeEventList, required this.place, }) : super(key: key); @override State<AttendeeList> createState() => _AttendeeListState(); } class _AttendeeListState extends State<AttendeeList> { late final Stream? _attendeeListStream; @override void initState() { super.initState(); String dbChild = "attendees/" + widget.place + "/"; final db = FirebaseDatabase.instance .ref() .child(dbChild) .orderByKey() .limitToLast(1500); _listStream = db.onValue; } @override Widget build(BuildContext context) { return StreamBuilder( stream: _attendeeListStream, builder: (context, AsyncSnapshot<dynamic> dbEvent) { if (dbEvent.hasError) { return CircularProgressIndicator(); } else if (dbEvent.hasData) { DataSnapshot dataSnapshot = dbEvent.data!.snapshot; List<AttendeeDetails> attendeeList = []; if (dataSnapshot.value != null) { (dataSnapshot.value as Map<dynamic, dynamic>) .forEach((key, value) { attendeeList.add(EventDetails.fromRTDB(value)); }); attendeeList .sort((a, b) => a.dateEST.compareTo(b.dateEST)); return Scaffold( body: ShowLists(placeEventList, attendeeList); } else { return PlaceDataNotAvailable(place: widget.place); } } else { return CircularProgressIndicator(); } }); } }
Widgets above can be called multiple times in the lifecycle of the app. The user comes to this screen by selecting a
place
on the initial screen, which executes the code in thePrepareList
stateful widget, which in its turn calls theAttendeeList
Stateful widget.I would like to emphisize that both
PrepareList
andAttendeeList
use streams. And each time the code in these widgets is executed, a large number of nodes (1500 for each widget) are downloaded from database.One execution can look as the following:
PrepareList("London");
And another execution may look as the following, presenting a new list of items on the same screen:
PrepareList("Manhattan");
And what I observe is:
When I run
PrepareList("London");
for the first time, it takes some time to (3 to 4 seconds) to see the content on the screen. Then I run thePrepareList("Manhattan");
, which also takes around 3 to 4 seconds to show the content. But when I runPrepareList("London");
again, the content appears on the screen very quickly, in ~1 second.In order to be able to call
PrepareList()
, I need to go to another screen, which means - in my understanding - that my stream subscription is cancelled each time I leave the screen associated to 2 widgets above. But is it so that the stream itself is NOT cancelled and the data remains in the memory?What I suspect: When using the app, as I call
PrepareList(...)
for multiple places (multiple times), it loads more and more data on the memory and it never cleans it. After a while, the app consumes all available memory and crashes by giving the error above, which tells me nothing meaningful. And asPrepareList(...)
is executed more and more when using the app, the Iphone 7 gets heated, which I can easily feel. I even tested with Iphone 12, which does not get heated as Iphone 7 but crashes as well.I even tried to add dispose as following to both classes:
@override void dispose() { super.dispose(); }
... but it still did not help.
Is my stream implementation true? Is what I suspect can be the underlying problem for this crash?
Any help would very highly be appreciated!
ADDENDUM 2:
I kept trying... I used the app in a way so that
PrepareList("...");
is triggered several times. I observed the memory usage in devtools as well. I can observe that the memory usage increases over time. And I got a new error this time saying something more concrete:[ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke [tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=LAST_ACK rcv_nxt=3749683210, snd_una=3584722489 [tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=CLOSED rcv_nxt=3749683210, snd_una=3584722489 * thread #46, name = 'DartWorker', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0) frame #0: 0x0000000108ef4d0c Flutter`dart::CompilerPass_TypePropagation::DoBody(dart::CompilerPassState*) const + 1644 Flutter`dart::CompilerPass_TypePropagation::DoBody: -> 0x108ef4d0c <+1644>: str xzr, [x20, x28, lsl #3] 0x108ef4d10 <+1648>: ldr x8, [x22, #0x48] 0x108ef4d14 <+1652>: cmp x24, x8 0x108ef4d18 <+1656>: b.ge 0x108ef4d84 ; <+1764> Target 0: (Runner) stopped. Lost connection to device.
This time, it says
EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0)
. Seemingly, the memory usage is increasing over time. Why is the memory not freed when the user leaves the screen on the phone? (I ask this question by suspecting that the stream still occupies the memory even after the user moves to another screen...)-
Pip about 2 yearsinvestigating similar issue on Flutter v2.16.1, ios v15.1. Are you using Firebase DB? I've noticed a Firebase query shortly before the error occurs on my end?
-
edn about 2 years@Pip Thanks for sharing your comment. Yes I do use Firebase. It is though impossible to locate where it is happening because it may crash at totally different irrelevant places of the app. That would be great to understand what this error means (e.g. does that mean insufficient memory?) so that I will have an idea about what to look at.
-
kuhnroyal about 2 yearsYou won't get an answer like this. You need to provide a lot more info on plugins and probably the full crash log from the device.
-
edn about 2 years@kuhnroyal How do I get the full crash log from the device?
-
kuhnroyal about 2 years
-
edn about 2 years@kuhnroyal I added the crash log from the phone now. Does it tell you anything? Please note that the question exceeded the total number of character limit so I provided the remaining part of the crash in an answer below.
-
edn about 2 years@kuhnroyal I added even more information to my question above. Any chance you can look at it?
-
edn about 2 years@RémiRousselet Any chance you can look at this question?
-
simple_code about 2 yearsEXC_BAD_ACCESS happens when objective C is trying to use an object that has already been freed. It could mean that there are too many UI widgets in memory at the same time? Are you using ListView.builder() or equivalent that recycles UI widgets? Maybe it's a good idea to separate the code where you're getting data from firebase from the code that is listing the data? Maybe you can try to build this to android or android emulator and see if you get a different error? Or maybe build it to web?
-
edn about 2 years@simple_code Thank you for your comment! Yes,I am using ListView.builder in parts of the code and the code that gets the data from Firebase is not necessarily separated from the code that lists the data. An yes, there are quite a number of UI widgets. I will start with trying to separate the Firebase implementation from UI implementation. EXC_BAD_ACCESS only happens occasionally at different part of the UI (not necessarily on the same page), which makes it extremely hard to localize where I should be focusing on. Any other comment or explanation more than welcome!!
-
-
edn about 2 yearsThank you very much for your comments. I did some of them and it was truly helpful. My original problem with EXC_BAD_ACCESS appeared to be related to another problem which I will explain in a separate answer.