第一章 高手筑基基础技能

1.1Java虚拟机

1.JVM内存模型

JVM共可划分为五大内存区域,如下所示

  • 虚拟机栈
  • 本地方法栈
  • 程序计数器
  • 方法区

其中,虚拟机栈,本地方法栈,程序计数器是线程私有的,堆和方法区是线程共享的

栈帧的组成

  • 局部变量表
  • 操作数栈
  • 动态链接
  • 方法出口

堆内存的划分

年轻代和老年代,一般老年代占比80% 年轻代占比20%

年轻代划分为:

  • Eden区
  • Survivor区
    • from
    • to

GC算法:

Minor GC(Young GC):新生代GC,即目标只是新生代的垃圾收集

Major GC(Old GC):老年代GC,即目标只是老年代的垃圾收集

Full GC:整堆收集,收集整个Java堆和方法区的垃圾收集

参考文章:https://blog.csdn.net/Alpha_Paser/article/details/82533128

面试题:一个大对象分配内存会直接进入老年代吗?

答案:会,大对象直接进入老年代,避免在新生代的Eden和Survivor区来回复制,产生大量内存复制操作

知识点:在JVM五大内存区域中,只有程序计数器是没有内存溢出的

2.垃圾回收算法

垃圾分析算法

  • 引用计数算法
  • 可达性分析算法

引用计数算法:每当一个对象被引用一次的时候,引用计数就加1,这种算法存在缺陷,如循环引用,即两个对象互相引用,引用计数都为1,导致无法被回收,故主流的Java虚拟机都没有采用引用计数算法

垃圾回收算法

  • 标记清除算法
  • 复制算法
  • 标记整理算法
  • 分代回收算法

标记清除算法:最基础的算法,标记要清理的对象,标记完成后,统一清理,效率虽然高,但是会存在内存碎片

标记复制算法:划分两块区域,垃圾回收时,将存活的对象复制到另一快区域,避免内存碎片,新生代内存回收就是采用这种算法,划分为Eden区和Survivor区,Survivor分为from和to两个区域,每次垃圾回收时,将Eden和from中存活的对象复制到to中

3.强引用,弱引用,软引用,虚引用

强引用:GC不会去回收强引用,宁愿抛出OOM

弱引用: 在系统内存不足时,GC才会考虑回收弱引用

软引用:软引用的对象只能生存到下一次GC之前,当垃圾收集器工作时,会回收软引用关联的对象

虚引用:随时可能被回收

4.两次标记和finalize

当一个对象在可达性分析算法中判定不可达后,并不会立即被垃圾回收器回收,而是会被标记一次,然后会调用finalize方法,可以在这个方法中自救,重新可达,即可避免被回收,接下来会进行第二次标记,后续就会被回收

第二章 Android面试题

2.1Handler消息机制

1.主线程Looper和MessageQueue的创建流程?

一切的一切得从activityThread说起,它是我们的主线程,或者说UI线程,但是它并不是一个thread类,activityThread与一个main方法,从这里开始启动我们的线程

1
2
3
4
public static void main(String[] args) {
Looper.prepareMainLooper();
Looper.loop();
}

Looper的创建流程图:

1
Looper.prepareMainLooper();  ->  prepare() -> new Looper() -> 将Looper对象放进threadLocal中

MessageQueue的创建(在new Looper的构造方法中):

1
mQueue = new MessageQueue(quitAllowed);

有此我与你分享完Looper和MessageQueue的创建

Looper的loop循环

在上一小节中我们看到了Looper创建完成后,调用了Looper.loop()方法,由此开启了loop循环,核心代码:

1
2
3
4
for (;;) {
Message msg = queue.next();//可能会阻塞,nativePollOnce
msg.target.dispatchMessage(msg);
}

解析:在loop循环中,会获取一条消息,然后调用target.dispatchMessage,这里的target就是handler

1
2
3
public void dispatchMessage(@NonNull Message msg) {
handleMessage(msg);
}

当我们创建handler,并重写了handleMessage,这里就会最终执行我们的代码

2.ThreadLocal是什么,以及在它消息机制中的作用?

含义:ThreadLocal是一个线程的存储类,提供了get和set方法,它是与线程绑定在一起的,在消息机制中的作用就是,用于存储当前线程的Looper对象,内部使用的ThreadLocalMap来存储,key为当前线程,value为looper对象

Looper的prepare方法:

1
2
3
private static void prepare(boolean quitAllowed) {
sThreadLocal.set(new Looper(quitAllowed));//将Looper存储在ThreadLocal中
}

Looper的myLooper方法:

1
2
3
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}