赞
踩
内存抖动指的是在短时间内大量对象被创建和销毁,导致频繁的垃圾回收(Garbage Collection, GC)活动。这种频繁的GC活动会占用大量的CPU资源,可能导致应用程序的卡顿或性能下降。
表现:内存曲线呈锯齿状。
内存泄露是指应用程序持有了不再需要的对象的引用,导致这些对象无法被垃圾回收器回收,从而占用了本可以被释放的内存空间。随着时间的推移,内存泄露会导致可用内存越来越少,最终可能导致应用程序崩溃或性能下降。
内存溢出是指应用程序尝试分配更多的内存空间,但系统无法满足这个请求,因为已经没有足够的内存空间可供分配。这通常会导致应用程序抛出OutOfMemoryError异常。
Java 内存结构:堆、虚拟机栈、方法区、程序计数器、本地方法栈。
Java 内存回收算法:
标记-清除算法缺点:标记和清除效率不高,会产生大量不连续的内存碎片。
复制算法:实现简单,运行高效。缺点:浪费一半空间。
标记-整理算法:避免标记-清理导致的内存碎片,避免复制算法的空间浪费。
Android内存弹性分配,分配值与最大值受具体设备影响。
Dalvik 回收算法和 ART 回收算法都是 Android 操作系统中用于内存管理的垃圾回收机制
Dalvik 回收算法:
Art 回收算法:
LMK机制:
public class ShakeActivity extends AppCompatActivity { private static Handler mHandler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); String str = ""; for (int i = 0; i < 10000000; i++) { str += i; } mHandler.sendEmptyMessageDelayed(1, 30); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_shake); } public void startClick(View view) { mHandler.sendEmptyMessage(1); } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } }
Memory Profiler 可以查看内存分配情况,点击“Record Java/Kotlin allocations"。
上面的含义:
下面的含义:
malloc()
或 new
运算符分配的对象数量。free()
或 delete
运算符解除分配的对象数量。上图分析:
这块地方 Allocations 和 Deallocations 的数值比较相近,同时 Shallow Size 比较大,说明可能频繁的创建和销毁对象。
点击后,可以查看调用栈信息,结合代码可以推测出 Handler 地方存在内存抖动问题。
public class CallbackManager {
public static ArrayList<Callback> sCallbacks = new ArrayList<>();
public static void addCallback(Callback callback) {
sCallbacks.add(callback);
}
public static void removeCallback(Callback callback) {
sCallbacks.remove(callback);
}
}
public class LeakActivity extends AppCompatActivity implements Callback { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_leak); ImageView imageView = findViewById(R.id.imageView); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.splash); imageView.setImageBitmap(bitmap); CallbackManager.addCallback(this); } @Override public void onOperate() { } }
添加依赖库:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
发生内存泄露后,LeakCanary会生成相关信息,并自动转储:
上图可知:是 LeakActivity 发生内存泄露了,并显示出引用链关系。
当然也可以生成 hprof 文件,通过 Profiler 工具查看具体信息:
上图可知:发生了10个泄露点,其中就有 LeakActivity,点击 LeakActivity 可以查看这内存泄露的对象,并查看引用链,可知是被ArrayList持有了。
使用完Bitmap后若不释放图片资源,容易造成内存泄露,从而导致内存溢出。
内存回收:
Bitmap.recycle()
进行Bitmap的回收。Bitmap的像素配置:
Config | 占用字节大小(byte) | 说明 |
---|---|---|
ALPHA_8 | 1 | 单透明通道 |
RGB_565 | 2 | 简易RGB色调 |
ARGB_8888 | 4 | 24位真彩色 |
RGBA_F16 | 8 | Android 8.0 新增(HDR) |
计算Btimap占用内存:
资源文件问题:
测试代码:
private void printBitmap(Bitmap bitmap, String drawable) { String builder = drawable + " Bitmap占用内存:" + bitmap.getByteCount() + " width:" + bitmap.getWidth() + " height:" + bitmap.getHeight() + " 1像素占用大小:" + getByteBy1px(bitmap.getConfig()); Log.e("TAG", builder); } private int getByteBy1px(Bitmap.Config config) { if (Bitmap.Config.ALPHA_8.equals(config)) { return 1; } else if (Bitmap.Config.RGB_565.equals(config)) { return 2; } else if (Bitmap.Config.ARGB_8888.equals(config)) { return 4; } return 1; }
// 逻辑密度 float density = metrics.density; // 物理密度 int densityDpi = metrics.densityDpi; Log.e("TAG", density + "-" + densityDpi); // 1倍图 Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.splash1); printBitmap(bitmap1, "drawable-mdpi"); // 2倍图 Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.splash2); printBitmap(bitmap2, "drawable-xhdpi"); // 3倍图 Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.splash3); printBitmap(bitmap3, "drawable-xxhdpi"); // 4倍图 Bitmap bitmap4 = BitmapFactory.decodeResource(getResources(), R.drawable.splash4); printBitmap(bitmap4, "drawable-xxxhdpi"); // drawable Bitmap bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.splash); printBitmap(bitmap5, "drawable"); /* 3.0-480 drawable-mdpi Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4 drawable-xhdpi Bitmap占用内存:9281844 width:1287 height:1803 1像素占用大小:4 drawable-xxhdpi Bitmap占用内存:4125264 width:858 height:1202 1像素占用大小:4 drawable-xxxhdpi Bitmap占用内存:2323552 width:644 height:902 1像素占用大小:4 drawable Bitmap占用内存:37127376 width:2574 height:3606 1像素占用大小:4 */
说明:
在 mdpi 的设备上 1dp1px,在 xhdpi 的设备上 1dp2px,在 xxhdpi 的设备上 1dp==3px。
因此当前设备是 xxhdpi,因此同一张图片在 xxhdpi 资源下宽是858,在 mdpi 资源下会放大3倍宽是2574,在 xhdpi 资源下会放大1.5倍宽是1287。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。