面试总结

最近的面试过程中遇到的面试题,涉及的方面比较广泛,大致分成了一下几个方面:
操作系统、Java、Android、计算机网络、其他(kotlin、react、安全之类的)。

操作系统相关:

进程和线程的区别

  • 进程是资源的分配和调度的一个独立单元,而线程是CPU调度的基本单元
  • 同一个进程中可以包括多个线程,并且线程共享整个进程的资源(寄存器、堆栈、上下文),一个进行至少包括一个线程。
  • 进程的创建调用fork或者vfork,而线程的创建调用pthread_create,进程结束后它拥有的所有线程都将销毁,而线程的结束不会影响同个进程中的其他线程的结束
  • 线程是轻量级的进程,它的创建和销毁所需要的时间比进程小很多,所有操作系统中的执行功能都是创建线程去完成的
  • 线程中执行时一般都要进行同步和互斥,因为他们共享同一进程的所有资源
  • 线程有自己的私有属性TCB,线程id,寄存器、硬件上下文,而进程也有自己的私有属性进程控制块PCB,这些私有属性是不被共享的,用来标示一个进程或一个线程的标志

线程调度算法

  • SCHED_OTHER:普通任务调度策略。
  • SCHED_FIFO:实时任务调度策略,先到先服务。一旦占用cpu则一直运行,直到有更高优先级任务到达或自己放弃。
  • SCHED_RR:实时任务调度策略,时间片轮转。当任务的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾
    具体涉及到N多内容,自行搜索

内存页面置换算法

为提高内存利用率,解决内存供不应求的问题,更加合理的使用内存,人们创造了分页式内存抽象。同时有一个虚拟内存的概念,是指将内存中暂时不需要的部分写入硬盘,看上去硬盘扩展了内存的容量,所以叫做“虚拟”内存。使用虚拟内存,应用程序可以使用比实际物理内存更大的内存空间。可以认为这个更大的内存空间就在硬盘上,只有将某一部分需要被用到时,才被写入真实内存;当它暂时不再被用到时,又被写回硬盘。分页式内存管理将物理内存分为等大的小块,每块大小通常为1K、2K、4K等,称为页帧;逻辑内存(使用虚拟内存技术扩大的内存,可认为其位于硬盘上)也被分为等大的小块,称为页;且页和页帧的大小一定是一样的,它是写入真实内存和写回硬盘最小单位。

** Optimal算法(最优算法) **
  从内存中移除永远都不再需要的页面或者说是未来最长时间内不再被访问的页面,如果这样的页面存在,则选择最长时间不需要访问的页面。采用最佳置换算法,可以保证较低的页面更新频率。从理论上讲,由于无法预知哪一个页面是未来最长时间内不再被访问的,因而该算法无法实现,但是可用来衡量其他算法。

** FIFO(First-In First-Out,先进先出)算法 **
该算法总是淘汰最早进入内存的页面,即选择在内存中停留时间最久的页面予以淘汰。
  这个算法的实现简单,只需要将进程已调入内存中的页面,按照先后顺序连接成一个队列,设置一个替换指针,总是指向最老的页面。
  但是该算法与进程实际的规律并不相适应,因为在进程中,有些页面经常被访问,比如:含有全局变量、常用函数、例程等的页面,FIFO不能保证这些页面不会被淘汰。
  正是由于没有考虑页面的重要性的问题,FIFO算法很容易将重要的页换出内存。

** Second Chance(第二次机会)算法 **
  为了避免FIFO算法将重要的页换出内存,Second Chance算法提供了一些改进。Second Chance算法在将页面换出内存前检查其使用位,如果其使用位为1,证明此页最近有被使用,猜测它还可能被使用,于是不把它置换出内存,但是把其使用位置为0,随后检查下一个页面,直到发现某页的使用位为0,将此页置换出内存。

** LRU(Least Recent Used, 最近最少使用)算法 **
  在之前的FIFO算法中,依据的是各个页面调入内存的时间,这并不能反映页面的真实使用情况。而LRU(Latest Recently Used)是根据页面调入内存之后的使用情况。由于无法预测页面未来的情况,所以只能利用“最近的过去”来作为预测未来的方法,LRU选择的是最近最久未使用的页面予以淘汰。
  该算法赋予每个页面一个访问字段,用来记录一个页面从上次被访问以来所经历的时间t,当需要淘汰一个页面的时候,选择现有页面中t的值最大的页面进行淘汰。
  LRU是一种优秀的页面置换算法,但是需要硬件的支持,为了了解一个进程在内存中各个页面各有多少时间未被进程访问,以及如何快速地知道哪一个页面是最近最久未使用的页面,需要 寄存器+栈 来支持。

java相关:

synchronized 和Lock的区别 以及实现原理

教科书式的应试回答:
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

| 类别 | synchronized | lock |
|: - :| :-: | : -: |
| 存在层次 | Java的关键字,在jvm层面上 | 是一个类|
| 锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
| 锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待|
| 锁状态 | 无法判断 | 可以判断|
| 锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可)|
|性能 | 少量同步 | 大量同步|

关于同步类(java.util.concurrent包下)的介绍可以看这个 http://blog.huangyuanlove.com/2018/03/20/Java%E5%B9%B6%E5%8F%91%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5/#more
关于Lock的底层实现可以看这篇:https://blog.csdn.net/Luxia_24/article/details/52403033
关于synchronized可以看这篇 https://blog.csdn.net/u012403290/article/details/64910926?locationNum=11&fps=1

Map、Set、List区别

这个自己翻一下源码看一下就好
Map-> HashMap(存入数据的过程,hash冲突了怎么办),TreeMap,LinkedHashMap
Set-> 使用Map实现的,set中存放的数据对应map中的key,value是内部的一个object字段
list-> ArrayList(数组),LinkedList(链表)区别,优缺点

Java虚拟机

这个主要是问内存区域,垃圾回收算法,可以参考这里:
JVM-内存分配与回收策略 http://blog.huangyuanlove.com/2017/04/07/JVM-%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D%E4%B8%8E%E5%9B%9E%E6%94%B6%E7%AD%96%E7%95%A5/
JVM-垃圾收集算法与实现 http://blog.huangyuanlove.com/2017/03/30/JVM-%E5%9E%83%E5%9C%BE%E6%94%B6%E9%9B%86%E7%AE%97%E6%B3%95/
JVM垃圾回收-对象已死? http://blog.huangyuanlove.com/2017/03/29/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6-%E5%AF%B9%E8%B1%A1%E5%B7%B2%E6%AD%BB%EF%BC%9F/
JVM内存区域 http://blog.huangyuanlove.com/2017/03/27/JVM%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F/

捕获子线程异常

1
2
3
4
5
6
7
8
9
10
11
12
new Thread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {

}
});
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {

}
});

如果一个线程没有显式的设置它的UncaughtExceptionHandler,JVM就会检查该线程所在的线程组是否设置了UncaughtExceptionHandler,如果已经设置,就是用该UncaughtExceptionHandler;否则查看是否在Thread层面通过静态方法setDefaultUncaughtExceptionHandler()设置了UncaughtExceptionHandler,如果已经设置就是用该UncaughtExceptionHandler;如果上述都没有找到,JVM会在对应的console中打印异常的堆栈信息。
具体情况可以看一下Java并发编程的艺术

线程池

这是个大坑,看源码吧ThreadPoolExecutor.java,还有就是常见线程池的比较。

单例模式 线程安全

单例的模式的N种写法,像什么double check、懒汉模式、静态内部类,还有就是枚举方式。这里会引出来volatile关键字,然后就是线程安全的问题。

Android相关:

多进程写sp的问题

还是线程安全的问题,因为SP不支持两个进程同事去执行写操作,否则会导致一定几率的数据丢失,这是因为SP底层通过读写xml文件来实现的,并发写显然是有问题的

sp底层实现

读写xml文件

Activity和Fragment生命周期,生命周期函数是谁调用的

声明周期函数是由ActivityThread调度的,具体逻辑封装在Instrumentation类里面。具体读一下这两个类就好。

Activity启动模式,启动过程

单独开了一篇博客http://blog.huangyuanlove.com/2018/07/26/Activity%E5%90%AF%E5%8A%A8%E6%A8%A1%E5%BC%8F%EF%BC%8C%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/#more

View点击事件分发流程

View事件分发:http://blog.huangyuanlove.com/2018/07/29/View事件分发/#more

View绘制过程

View绘制过程:http://blog.huangyuanlove.com//2018/07/29/View绘制过程/#more

Handler

Android的消息机制: http://blog.huangyuanlove.com/2017/03/23/Android%E7%9A%84%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6/#more

省电优化

不打扰系统休眠(不监听电量变化等,三方的可以hook oop hook住oncreat)
不做不必要的动作(界面后台要停止动画)

广播的底层实现(binder)

Android 广播 :http://blog.huangyuanlove.com/2018/07/31/Android%E5%B9%BF%E6%92%AD/

计算机网络相关:

websocket为什么能保持长连接

http、tcp/udp、tcp怎么解决拥塞、滑动窗口、udp

这个可以看谢希仁的《计算机网络》第六版,tcp:http://blog.huangyuanlove.com/2017/07/03/TCP%E5%8D%8F%E8%AE%AE%E7%9A%84%E7%AE%80%E5%8D%95%E7%90%86%E8%A7%A3/#more 这里简单的记录了一下比较重要的东西。

其他:

加固原理 加的什么壳
kt var val区别 when中的 is in


面试总结
https://blog.huangyuanlove.com/2018/06/03/面试总结/
作者
HuangYuan_xuan
发布于
2018年6月3日
许可协议
BY HUANG兄