2

I have an activity that constantly (several times per second) sends byte[] data to a service via AIDL. The service forwards these messages using a message-bundle* to a network thread's handler. The network thread also constantly receives data, which it sends to the service's handler as message-bundle. The service sends this bundle to other activities through AIDL.

As you can tell, there is a lot of memory allocated for the bundles.

After viewing my memory allocations on DDMS I see that memory is allocated for:

  • the bundle being initialized
  • the hash map being created
  • the byte[] data I am adding as an entry to the hash table

I would like to reduce memory allocations to as little as possible.

Is there a better way to send byte[ ] across activities/threads than through messages with bundles?

*By message-bundle, I mean a message with a bundle as the data

Tried: to make a pool of bundle objects, but that didn't work out too well - hard to manage between activities and I don't know how to pre-allocate it with byte[] data...

f20k
  • 3,106
  • 3
  • 23
  • 32
  • I do not think so but then I am no android expert; if there is a way then that would mean big things for my Android apps. Of course you could just try recycling the objects that you create. Like the byte buffer with a Singleton recycling class. – Robert Massaioli May 27 '11 at 05:39
  • 1
    This is what I mean by Recycling btw. Acquiring and releasing resources that are just POJO's: http://www.javaworld.com/javaworld/javatips/jw-javatip78.html – Robert Massaioli May 27 '11 at 05:40

2 Answers2

3

Are those byte arrays that big that you really need to worry about? Without knowing anything of your app, I would guess that the bottleneck in the app you're describing will be in the network handler, rather than the messaging between the Service and the Activity. Unless you're leaking the Bundles somewhere, the GC should take care of all unused memory and restore it for your app to reuse.

If anything, I would try to keep those arrays small enough (to avoid allocating and recycling large amounts of memory at once) and send several small messages instead. That way, the app does not consume the available memory all at once and let the GC do its job with a relaxed schedule (i.e., when the CPU is idle) instead of forcing GC to collect the objects immediately.

Of course, what is "too big" or "small enough" is something you should test in your own app and measure what (if any) is the gain with one approach or other.

Update:

Copying verbatim from an android dev site article:

In a performance-sensitive code path, such as the layout or drawing method of a view or the logic code of a game, any allocation comes at a price. After too many allocations, the garbage collector will kick in and stop your application to let it free some memory. Most of the time, garbage collections happen fast enough for you not to notice. However, if a collection happens while you are scrolling through a list of items or while you are trying to defeat a foe in a game, you may suddenly see a drop in performance/responsiveness of the application. It's not unusual for a garbage collection to take 100 to 200 ms. For comparison, a smooth animation needs to draw each frame in 16 to 33 ms. If the animation is suddenly interrupted for 10 frames, you can be certain that your users will notice.

Based on your comment, I would reconsider whether the GC is relevant in your case. Heap limit may vary between 16 and 32 MB (and the upper limit is probably growing). Assuming 10 allocations per second, 64 bytes per allocation, you would allocate 1 MB every 30 minutes (approx). If the GC takes 200 ms to free that memory, the effect will be negligible. I understand that the numbers here do not consider the memory needed for the hash map nor the bundle, but they are still relevant to give you an idea of the orders of magnitude that we're talking about. Now, consider how much time does a network request to grab those 64 bytes from the net. Even if it requires just 50 ms, that is 1/4 to 1/2 of the time needed by the GC, every net request. That does not even consider the time required by your Activity to process that data.

So, in conclusion, before restructuring your code in an attempt to improve its efficiency, make sure that the step you're modifying is actually a bottleneck, or all your effort might be wasted. Just my $0.02.

Community
  • 1
  • 1
Aleadam
  • 40,203
  • 9
  • 86
  • 108
  • The byte arrays are [64] in size. The reason being is that they contain variable amounts of data < [64]. So I pass in a byte[64] and figure out how much to transmit to the server. I understand that the GC will take care of it. However, I'm trying to run the GC as little as possible. – f20k May 27 '11 at 12:40
  • thanks for the help. I think I will look more into profiling my code to find the bottlenecks that I can improve. – f20k May 27 '11 at 15:59
1

I often use pools on embedded stuff for almost exactly this reason - to provide some 'flow control', (if the pool is empty, a thread has to wait on it until a message is released from somewhere else), and to prevent runaway malloc/new depleting what little heap I have. Managing the pool is easier if each pooled message contains a private reference to its pool. This means that an activity/thread does not need any pool reference to be explicitly passed to it to enable it to release a message back to the pool - a 'release' method of the message can do it without any parameters and, if for some reason there is more than one pool, there is no danger of a message being released back onto the wrong one.

Rgds, Martin

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • I think the idea of a private reference is useful. I will try it out when I have time. – f20k May 27 '11 at 12:44