18 KiB
18 KiB
内存优化
目录
内存泄漏
什么是内存泄漏?
内存泄漏是指程序在运行过程中,由于某些原因导致无法释放已经不再使用的内存,造成内存浪费,最终可能导致 OOM(OutOfMemoryError)。
常见内存泄漏场景
1. 静态变量持有 Context
// ❌ 错误示例
public class MyActivity extends AppCompatActivity {
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sContext = this; // 静态变量持有 Activity,导致泄漏
}
}
// ✅ 正确做法
public class MyActivity extends AppCompatActivity {
private static Context sContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sContext = getApplicationContext(); // 使用 Application Context
}
@Override
protected void onDestroy() {
super.onDestroy();
sContext = null; // 及时释放
}
}
2. Handler 内存泄漏
// ❌ 错误示例
public class MyActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Handler 持有 Activity 的隐式引用
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
// 如果 Activity 已销毁,但 Handler 还在处理消息,导致泄漏
}
}, 10000);
}
}
// ✅ 正确做法
public class MyActivity extends AppCompatActivity {
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (MyActivity.this.isFinishing()) {
return; // Activity 已销毁,不处理消息
}
// 处理消息
}
};
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null); // 移除所有消息
}
}
// ✅ 使用静态内部类 + WeakReference
public class MyActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
private WeakReference<MyActivity> mActivity;
MyHandler(MyActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MyActivity activity = mActivity.get();
if (activity == null || activity.isFinishing()) {
return;
}
// 处理消息
}
}
}
3. 单例模式持有 Context
// ❌ 错误示例
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context; // 持有 Activity Context
}
public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}
// ✅ 正确做法
public class Singleton {
private static Singleton instance;
private Context mContext;
private Singleton(Context context) {
this.mContext = context.getApplicationContext(); // 使用 Application Context
}
public static Singleton getInstance(Context context) {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(context);
}
}
}
return instance;
}
}
4. 内部类持有外部类引用
// ❌ 错误示例
public class MyActivity extends AppCompatActivity {
private MyAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTask = new MyAsyncTask(); // 内部类持有 Activity 引用
mTask.execute();
}
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... voids) {
// 耗时操作
return null;
}
}
}
// ✅ 正确做法:使用静态内部类
public class MyActivity extends AppCompatActivity {
private MyAsyncTask mTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTask = new MyAsyncTask(this);
mTask.execute();
}
private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<MyActivity> mActivity;
MyAsyncTask(MyActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
protected Void doInBackground(Void... voids) {
// 耗时操作
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
MyActivity activity = mActivity.get();
if (activity == null || activity.isFinishing()) {
return;
}
// 更新 UI
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mTask != null) {
mTask.cancel(true);
}
}
}
5. 监听器未注销
// ❌ 错误示例
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
// 忘记注销监听器
}
}
// ✅ 正确做法
public class MyActivity extends AppCompatActivity {
private SensorManager mSensorManager;
private Sensor mSensor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mSensorManager != null) {
mSensorManager.unregisterListener(this); // 注销监听器
}
}
}
6. 集合类持有对象引用
// ❌ 错误示例
public class MyActivity extends AppCompatActivity {
private static List<View> sViews = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = findViewById(R.id.view);
sViews.add(view); // 静态集合持有 View,View 持有 Activity,导致泄漏
}
}
// ✅ 正确做法
public class MyActivity extends AppCompatActivity {
private static List<WeakReference<View>> sViews = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = findViewById(R.id.view);
sViews.add(new WeakReference<>(view));
}
@Override
protected void onDestroy() {
super.onDestroy();
// 清理集合
sViews.clear();
}
}
内存抖动
什么是内存抖动?
内存抖动是指短时间内频繁分配和回收内存,导致 GC 频繁执行,影响应用性能。
内存抖动的原因
- 频繁创建对象:在循环中创建大量临时对象
- 字符串拼接:使用
+拼接字符串 - 集合扩容:集合频繁扩容导致内存分配
内存抖动示例
// ❌ 错误示例:频繁创建对象
for (int i = 0; i < 10000; i++) {
String str = new String("Hello" + i); // 每次循环都创建新对象
list.add(str);
}
// ✅ 正确做法:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append("Hello").append(i);
list.add(sb.toString());
sb.setLength(0); // 清空 StringBuilder
}
// ❌ 错误示例:字符串拼接
String result = "";
for (int i = 0; i < 1000; i++) {
result += "item" + i; // 每次拼接都创建新对象
}
// ✅ 正确做法:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("item").append(i);
}
String result = sb.toString();
内存分析工具
1. LeakCanary
集成 LeakCanary
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
使用
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
}
}
2. Android Studio Memory Profiler
- 实时查看内存使用
- 查看内存分配
- 查看内存泄漏
- 生成 Heap Dump
3. MAT (Memory Analyzer Tool)
- 分析 Heap Dump
- 查找内存泄漏
- 查看对象引用关系
4. adb 命令
# 查看内存信息
adb shell dumpsys meminfo <package_name>
# 查看进程内存
adb shell dumpsys meminfo <pid>
# 强制 GC
adb shell am force-stop <package_name>
内存优化方案
1. 使用对象池
public class ObjectPool<T> {
private Queue<T> pool = new LinkedList<>();
private Supplier<T> factory;
private int maxSize;
public ObjectPool(Supplier<T> factory, int maxSize) {
this.factory = factory;
this.maxSize = maxSize;
}
public T acquire() {
T obj = pool.poll();
if (obj == null) {
obj = factory.get();
}
return obj;
}
public void release(T obj) {
if (pool.size() < maxSize) {
pool.offer(obj);
}
}
}
// 使用
ObjectPool<StringBuilder> pool = new ObjectPool<>(
StringBuilder::new, 10
);
StringBuilder sb = pool.acquire();
// 使用 sb
pool.release(sb);
2. 使用弱引用
// WeakReference:GC 时会被回收
WeakReference<Activity> weakRef = new WeakReference<>(activity);
Activity activity = weakRef.get();
if (activity == null) {
// 已被回收
}
// SoftReference:内存不足时会被回收
SoftReference<Bitmap> softRef = new SoftReference<>(bitmap);
Bitmap bitmap = softRef.get();
if (bitmap == null) {
// 已被回收
}
3. 及时释放资源
public class MyActivity extends AppCompatActivity {
private Bitmap mBitmap;
private FileInputStream mInputStream;
@Override
protected void onDestroy() {
super.onDestroy();
// 释放 Bitmap
if (mBitmap != null && !mBitmap.isRecycled()) {
mBitmap.recycle();
mBitmap = null;
}
// 关闭流
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
mInputStream = null;
}
}
}
4. 使用 SparseArray 替代 HashMap
// HashMap<Integer, Object> 会创建 Integer 对象
HashMap<Integer, String> map = new HashMap<>();
// SparseArray 避免自动装箱
SparseArray<String> sparseArray = new SparseArray<>();
sparseArray.put(1, "value");
5. 避免在 onDraw 中创建对象
// ❌ 错误示例
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint(); // 每次绘制都创建新对象
canvas.drawText("Hello", 0, 0, paint);
}
// ✅ 正确做法
public class MyView extends View {
private Paint mPaint; // 复用 Paint 对象
public MyView(Context context) {
super(context);
mPaint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText("Hello", 0, 0, mPaint);
}
}
图片内存优化
1. 图片压缩
public Bitmap compressBitmap(Bitmap bitmap, int maxWidth, int maxHeight) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
if (scale < 1.0f) {
int newWidth = Math.round(width * scale);
int newHeight = Math.round(height * scale);
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
}
return bitmap;
}
2. 使用合适的图片格式
- WebP:压缩率高,支持透明
- PNG:无损压缩,适合图标
- JPEG:有损压缩,适合照片
3. 使用 inSampleSize
public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
int halfHeight = height / 2;
int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
4. 使用 RGB_565
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565; // 减少内存占用
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
5. 及时回收 Bitmap
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
对象池
实现对象池
public class RecyclerPool<T> {
private final Queue<T> pool = new LinkedList<>();
private final Supplier<T> factory;
private final Consumer<T> reset;
private final int maxSize;
public RecyclerPool(Supplier<T> factory, Consumer<T> reset, int maxSize) {
this.factory = factory;
this.reset = reset;
this.maxSize = maxSize;
}
public T acquire() {
T obj = pool.poll();
if (obj == null) {
obj = factory.get();
}
return obj;
}
public void release(T obj) {
if (obj == null) {
return;
}
reset.accept(obj);
if (pool.size() < maxSize) {
pool.offer(obj);
}
}
public void clear() {
pool.clear();
}
}
// 使用示例
RecyclerPool<StringBuilder> pool = new RecyclerPool<>(
StringBuilder::new,
sb -> sb.setLength(0),
10
);
StringBuilder sb = pool.acquire();
sb.append("Hello");
// 使用完毕
pool.release(sb);
面试常见问题
Q1: 什么是内存泄漏?如何避免?
答案:
- 内存泄漏是指程序无法释放不再使用的内存
- 常见原因:静态变量持有 Context、Handler 未移除消息、监听器未注销等
- 避免方法:使用 Application Context、使用 WeakReference、及时注销监听器等
Q2: Handler 为什么会导致内存泄漏?
答案:
- Handler 持有 Activity 的隐式引用
- Message 持有 Handler 的引用
- MessageQueue 持有 Message 的引用
- 如果 Handler 还有未处理的消息,Activity 就无法被回收
Q3: 如何检测内存泄漏?
答案:
- 使用 LeakCanary 自动检测
- 使用 Android Studio Memory Profiler
- 使用 MAT 分析 Heap Dump
- 使用 adb dumpsys meminfo 命令
Q4: 什么是内存抖动?如何避免?
答案:
- 内存抖动是短时间内频繁分配和回收内存
- 避免方法:使用对象池、使用 StringBuilder、避免在循环中创建对象
Q5: 如何优化图片内存?
答案:
- 使用合适的图片格式(WebP、PNG、JPEG)
- 使用 inSampleSize 压缩
- 使用 RGB_565 减少内存
- 及时回收 Bitmap
- 使用图片加载库(Glide、Picasso)
Q6: Context 的使用注意事项?
答案:
- Activity Context:生命周期短,不能长期持有
- Application Context:生命周期长,可以长期持有
- 静态变量、单例等应使用 Application Context
Q7: 如何优化内存使用?
答案:
- 避免内存泄漏
- 使用对象池复用对象
- 使用弱引用
- 及时释放资源
- 优化图片内存
- 使用 SparseArray 替代 HashMap
总结
内存优化是 Android 性能优化的重要部分,主要从以下几个方面入手:
- 避免内存泄漏:注意 Context、Handler、监听器的使用
- 避免内存抖动:减少对象创建,使用对象池
- 优化图片内存:压缩、使用合适格式、及时回收
- 使用工具检测:LeakCanary、Memory Profiler、MAT
通过合理的内存优化,可以避免 OOM,提升应用性能。
最后更新:2024年