Mobile Performance Profiling
Identify and fix performance bottlenecks in mobile applications. Covers CPU profiling, memory leak detection, frame rendering analysis, network waterfall optimization, battery consumption tracking, and the patterns that keep mobile apps fast and responsive.
Mobile performance is existential: 53% of users abandon apps that take longer than 3 seconds to load. Unlike web applications where you control the server, mobile performance depends on the device in the user’s hand — which might be a 5-year-old phone on a 3G network. Profiling tells you exactly where time is spent so you can optimize what matters.
Critical Metrics
App Launch:
Cold start: < 2 seconds (app not in memory)
Warm start: < 1 second (app in background)
Hot start: < 500ms (app recently used)
UI Rendering:
Frame rate: 60 FPS target (16.7ms per frame)
Jank: Any frame taking > 16.7ms
Jank budget: < 1% of frames janky
Memory:
Baseline: < 100MB for typical app
Peak: < 200MB (avoid OOM kills on older devices)
Leaks: Zero retained objects after navigation
Network:
First meaningful paint: < 1.5 seconds
API response handling: < 100ms processing time
Offline capability: Core features work without network
Battery:
Background drain: < 1% per hour when idle
Active use: Comparable to native apps
Location/sensor: Minimal polling frequency
CPU Profiling
// iOS: Instruments Time Profiler
// 1. Profile > Time Profiler
// 2. Identify heaviest stack traces
// Common CPU bottleneck: Main thread blocking
class ProductListViewController: UIViewController {
// BAD: JSON parsing on main thread
func loadProducts() {
let data = try! Data(contentsOf: apiURL) // Network on main thread!
let products = try! JSONDecoder().decode([Product].self, from: data)
tableView.reloadData()
}
// GOOD: Background processing, main thread for UI only
func loadProducts() {
Task {
let products = try await api.fetchProducts() // Background
await MainActor.run {
self.products = products
self.tableView.reloadData() // UI update on main thread
}
}
}
}
Memory Leak Detection
// Android: LeakCanary + Memory Profiler
// Common leak: Activity reference in static/singleton
class Analytics {
companion object {
// BAD: Holds reference to Activity (leaks entire Activity)
var currentActivity: Activity? = null
}
}
// GOOD: Use WeakReference or application context
class Analytics {
companion object {
var currentActivity: WeakReference<Activity>? = null
}
}
// Common leak: Unregistered listeners
class LocationTracker {
// BAD: Never unregistered
fun startTracking(activity: Activity) {
locationManager.requestLocationUpdates(listener)
}
// GOOD: Lifecycle-aware
fun startTracking(lifecycle: Lifecycle) {
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
locationManager.requestLocationUpdates(listener)
}
override fun onStop(owner: LifecycleOwner) {
locationManager.removeUpdates(listener)
}
})
}
}
List Rendering Optimization
// React Native: FlatList optimization
// BAD: Re-renders entire list on any change
<FlatList
data={products}
renderItem={({ item }) => <ProductCard product={item} />}
/>
// GOOD: Optimized for large lists
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={(item) => item.id}
// Windowing: Only render visible items
windowSize={5} // Render 5 screens worth
maxToRenderPerBatch={10} // Render 10 items per batch
initialNumToRender={10} // Start with 10 items
// Prevent unnecessary re-renders
getItemLayout={(data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
})}
// Memoize render function
removeClippedSubviews={true}
/>
// Memoized component
const ProductCard = React.memo(({ product }) => (
<View style={styles.card}>
<Image source={{ uri: product.image }} />
<Text>{product.name}</Text>
<Text>{product.price}</Text>
</View>
));
Anti-Patterns
| Anti-Pattern | Consequence | Fix |
|---|---|---|
| Profile only on flagship devices | Poorest users have worst experience | Profile on low-end target devices |
| Optimize without profiling | Fix wrong bottleneck | Always profile first, optimize second |
| Images at original resolution | Memory bloat, slow rendering | Resize images to display dimensions |
| Synchronous network on main thread | UI freezes, ANR (Android Not Responding) | All network operations off main thread |
| No memory monitoring in production | Leaks discovered by user crashes | Crash reporting with memory diagnostics |
Mobile performance is a feature — the most important feature. A beautiful app that stutters, drains battery, and crashes on older devices will be uninstalled. Profile on real devices, optimize the critical path, and never block the main thread.