第十天笔记
内存泄漏
1. Java垃圾回收机制
1.1 什么是垃圾回收
- 垃圾回收(
GC
)是由Java虚拟机(JVM
)垃圾回收器提供的一种内存回收机制。 - 当内存空间或内存占用过高时,系统会回收那些没有引用的对象。
JVM内存模型
- 运行时数据区域:包括方法区、堆、栈、本地方法栈和程序计数器等。
- 堆内存:
- 新生代(
Young Generation
):分为Eden区和两个Survivor
区(S0、S1)。 - 老年代(
Old Generation
)。
- 新生代(
- 方法区(
MetaSpace
):存储类元数据、方法信息等。
1.2 判断对象是否是垃圾的算法
1.2.1 引用计数算法
- 对象被创建后,系统为其初始化引用计数器。
- 每当对象被引用时,计数器加1,引用失效时,计数器减1。
- 当计数器为0时,对象不再被引用,可以进行回收。
- 优点:判断简单,效率高。
- 缺点:无法避免循环引用。
1.2.2 可达性分析法
- 从GC Roots出发,逐步遍历到所有可达的对象。
- 这些对象称为可达对象,不可达的对象则被标记为垃圾,需要回收。
GC Roots包括的对象:
- 虚拟机栈中引用的对象(栈帧中的本地变量表)。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中
JNI
(Native
方法)引用的对象。
Java中的对象引用
Java中的对象引用主要分为四种类型:
1. 强引用 (Strong Reference)
- 定义: 例如
Object obj = new Object()
,这是Java程序中最常见的引用类型。 - 特点: 强引用是不可回收的资源,垃圾回收器不会回收它。当内存空间不足时,JVM宁愿抛出
OutOfMemoryError
错误,也不会回收强引用的对象。
2. 软引用 (Soft Reference)
- 定义: 软引用是一种相对强引用有所弱化的引用。
- 特点: 可以让对象避免一些垃圾收集。只有当JVM认为内存不足时,才会尝试回收软引用指向的对象。在抛出
OutOfMemoryError
之前,JVM会确保回收软引用的对象。软引用通常用于实现内存敏感的缓存,如果有足够内存,软引用对象会被保留。
3. 弱引用 (Weak Reference)
- 定义: 弱引用的强度比软引用更弱。
- 特点: 无论内存是否充足,当JVM进行垃圾回收时,都会回收被弱引用关联的对象。
4. 虚引用 (Phantom Reference)
- 定义: 虚引用也称为幽灵引用或幻象引用,是最弱的一种引用关系。
- 特点: 虚引用的存在不会对对象的生命周期构成影响,无法通过虚引用获得对象实例。唯一的目的是在对象被垃圾回收器回收时收到系统通知。
图示总结
- 强引用: 只有在没有空闲内存时才会被回收。
- 软引用: 在内存不足时会被回收。
- 弱引用: 在垃圾回收检测时立即回收。
- 虚引用: 不会直接影响对象生命周期,但能在对象被回收时收到通知。
这个图示展示了不同引用类型如何与垃圾回收器交互,以及在垃圾回收后的结果。
初识ANR
主线程执行耗时操作
当主线程执行耗时操作(如大量计算、循环、递归、IO操作等)会导致ANR。
示例代码:
mBind.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
while (true) {
Log.i("TAG", "主线程执行耗时操作");
try {
// 执行耗时操作
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
死锁
当对象锁被其他线程持有,主线程长时间无法获取当前对象锁,会导致ANR。
示例代码:
public class MainThreadAnrActivity extends BaseActivity<ActivityHandlerBinding> {
private final Object object = new Object();
private final String TAG = MainThreadAnrActivity.class.getSimpleName();
private boolean taskComplete = false;
@Override
protected void bindListener() {
super.bindListener();
mBind.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 需要使用子线程执行的结果
synchronized (object) {
Log.i(TAG, "死锁导致ANR " + taskComplete);
}
}
});
new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
try {
Thread.sleep(30 * 1000);
taskComplete = true;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}
解决方法
- 使用
ReentrantLock
代替synchronized
,同时tryLock()
增加最大等待时间,超过最大等待时间主动释放锁,避免出现ANR。
示例代码:
ReentrantLock lock = new ReentrantLock();
lock.tryLock(3000, TimeUnit.MICROSECONDS);
android
长生命周期:application
, service
content provider
作业