Files
mkdocs/docs/android面试/性能优化/内存优化.md
2026-01-15 11:53:37 +08:00

18 KiB
Raw Permalink Blame History

内存优化

目录


内存泄漏

什么是内存泄漏?

内存泄漏是指程序在运行过程中,由于某些原因导致无法释放已经不再使用的内存,造成内存浪费,最终可能导致 OOMOutOfMemoryError

常见内存泄漏场景

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); // 静态集合持有 ViewView 持有 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 频繁执行,影响应用性能。

内存抖动的原因

  1. 频繁创建对象:在循环中创建大量临时对象
  2. 字符串拼接:使用 + 拼接字符串
  3. 集合扩容:集合频繁扩容导致内存分配

内存抖动示例

// ❌ 错误示例:频繁创建对象
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. 使用弱引用

// WeakReferenceGC 时会被回收
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: 如何检测内存泄漏?

答案:

  1. 使用 LeakCanary 自动检测
  2. 使用 Android Studio Memory Profiler
  3. 使用 MAT 分析 Heap Dump
  4. 使用 adb dumpsys meminfo 命令

Q4: 什么是内存抖动?如何避免?

答案:

  • 内存抖动是短时间内频繁分配和回收内存
  • 避免方法:使用对象池、使用 StringBuilder、避免在循环中创建对象

Q5: 如何优化图片内存?

答案:

  1. 使用合适的图片格式WebP、PNG、JPEG
  2. 使用 inSampleSize 压缩
  3. 使用 RGB_565 减少内存
  4. 及时回收 Bitmap
  5. 使用图片加载库Glide、Picasso

Q6: Context 的使用注意事项?

答案:

  • Activity Context生命周期短不能长期持有
  • Application Context生命周期长可以长期持有
  • 静态变量、单例等应使用 Application Context

Q7: 如何优化内存使用?

答案:

  1. 避免内存泄漏
  2. 使用对象池复用对象
  3. 使用弱引用
  4. 及时释放资源
  5. 优化图片内存
  6. 使用 SparseArray 替代 HashMap

总结

内存优化是 Android 性能优化的重要部分,主要从以下几个方面入手:

  1. 避免内存泄漏:注意 Context、Handler、监听器的使用
  2. 避免内存抖动:减少对象创建,使用对象池
  3. 优化图片内存:压缩、使用合适格式、及时回收
  4. 使用工具检测LeakCanary、Memory Profiler、MAT

通过合理的内存优化,可以避免 OOM提升应用性能。


最后更新2024年