[旧文归档]面试知识点整理
Date: 2020年3月25日 GMT+8 下午10:49:44
MAYI
- Dex2oat:插件啥时候做dex2oat,如何避免,优化方案
// 最终C层去调用dex的时候会调用OpenDexFromOat,会判断有没有对应的oat文件
Runtime::Current()->IsDex2OatEnabled()
https://juejin.im/post/5c3462a8f265da61602d1b26
- android IPC机制详解(aidl、序列化)等
- sychronized & sychronized static 的区别
- recyclerview性能优化
- AMS、WMS
- Flutter跨端能力
- Android渲染机制
- https://www.jianshu.com/p/1ef2a9e5aa91
- https://www.androidperformance.com/2019/10/22/Android-Choreographer/
- JNI层:
- https://blog.csdn.net/hackooo/article/details/48395765
- 类的加载流程细化:
- AtomicInteger CAS操作与volatile的区别
- Volatile的可见性并不能保证类似++操作的原子性
- 但是使用synchronized性能损耗严重
- 因此引入了Atomic类,采用了Compare and Swap
- CAS主要是循环比较看当前值是否与预期值相同,如果相同。就直接设置进去,否则就说明有其他线程进行了修改,直到比较为true,就设置新值进去
-
- <https://juejin.im/post/5a73cbbff265da4e807783f5>
- 最终借助CPU指令,实质上还是个原子操作,
## 多线程问题复习
- 操作系统是以进程为单位进行资源分配的,但是 CPU 这个资源却比较特殊,CPU 的分配是以线程为单位的,这是因为具体占用 CPU 运行的是进程中的线程
- 守护线程 VS 用户线程(无用户线程 JVM就会退出)
- 线程之间的协作或者说是通信(比如生产者-消费者模型),常用的就是需要个同步设施来完成公共资源的访问或者读写。常见的同步措施有哪些
- 一种是寄托于公共资源本身(object本身),比如object的wait、notify、notifyAll等,这些方法本身的调用需要首先获取到monitor,也就是常在比如Synchronized代码块里面才能调用,否则会抛出状态异常。其实内部实现就是有一个monitor对应的AQS,不断的通过CPU时间片驱动,去不断的运转起来。
- 以object本身提供的方法来实现一个简单的生产者消费者模型
- 线程的中断模型:interrupt到底发生了什么
- Thread.interrupt() 仅仅设置了中断标志位后立即返回:抛出interrupted Exception
- 根据中断标志位,做一些其他例子
- 线程死锁问题的本质:竞争同一资源,互相不释放对方的占有条件
- 互斥条件
- 请求并持有条件
- 不可等待条件
- 环路条件
- Java内存模型(主存模型),CPU缓存,数据总线
- Synchronized & volatile语义
- Synchronized 重量级锁,通过monitorenter,monitorexit来实现,在monitor中标记当前线程,主要是保证可见性和原子性
- Volatile,主要是保证可见性,在轻量级的情况下,通过lock指令,锁缓存,另缓存的读取和写入都失效,跟主内存打交道。不能保证原子性,只能保证可见性。
- AtomicLong等类型中的CAS(无锁底层指令)实现:
- 使用锁必然有性能方面的损耗,线程上下文切换和重新调度的考量,虽然也提供了volatile非阻塞的操作,但不能保证原子性,因此Java还提供了一种基于底层硬件指令实现的原子操作,compare and swap
- 在unsafe.compareAndSwap实现里面:
boolean compareAndSwap(Object obj, long valueOffset, long expect, long update) {
```
//传入四个参数:对象内存地址,对象中变量的偏移量,变量预期值,变量新值
如果对象obj中内存偏移量为valueOffset位置的标量值为expect则使用新值update替换旧expect,这个底层处理器提供了一个原子性指令
//所谓便宜量,你可以理解为当前对象的某一属性的内存地址
while(compare and swap)
\\}
- 自旋操作,本质是乐观锁,使用CPU资源来减少锁阻塞带来的开销,是一种资源的替换
- ThreadLocal,为什么支持多线程不同副本
- 本质是通过当前线程去获取这个线程的ThreadLocals,Thread本身带一个threadLocals变量,类型为ThreadLocalMap的东西,其实是HashMap的变种,ThreadLocals是每个线程各自的变量,因此每当set的时候,其实是在给各自的ThreadLocals在设置。因此是线程隔离的。
- ThreadLocal容易引入内存泄漏问题,要记得手动remove掉
- CopyOnWriteArrayList
- 通过RentrantLock独占锁对当前的array数组加锁,保证只有个线程操作,
- 同时拷贝修改
- CountDownLatch使用场景:之前未出现之前通过join实现,一个任务拆分成多个子任务,等待完成
- countdownLatch(2)
- CountDownLatch.countDown()
- CountDownLatch.await()
- 内部实现原理:有一个AQS同步器,通过set/get state
- Synchronized 可重入实现
- 线程死锁
- 两个或两个以上的进程或者线程,相互竞争资源,或者彼此通信,造成的一种阻塞现象,若无外力,将无法打破这种。
- 产生条件:互斥条件,请求保持,循环等待,不可剥夺
- 如何避免:打破以上条件
- Synchronized VS RentrantLock
- 二者均可实现同步功能
- Synchronized是JVM自身隐性的完成加锁和解锁、Lock需要我们手动lock,unlock
- synchronized不可中断,除非异常,ReentrantLock可有中断,lock里面可以调用interrupt
- 加锁是否公平,synchronize的是非公平锁,而ReentrantLock可通过参数成为公平锁,所谓公平不公平是指当前等待队列里的线程能否按队列顺序获取到锁
- AtomicInteger怎么实现原子操作的
- 通过CAS指令,底层完成
- 缺点是:通过消耗CPU资源来抵消加锁的性能开销,只能保证单个共享变量的原子操作,存在ABA问题
- Java中的线程状态流转:
- new-runnable-blocked-waiting-timewaiting-termnatied
- 使用线程池的有点及其核心参数
- 如何设置线程池核心线程数,2N +1
2N
N+1
- 如何设置线程池核心线程数,2N +1
- AQS 实现:
- 如果被请求的资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态;如果被请求的资源被占用,则需要一套线程阻塞等待以及唤醒分配的机制,该机制基于一个 FIFO(先进先出)的等待队列实现。
- 可以理解为一个构件同步器的框架,用于处理,当一个线程访问一个共享资源的时候,如果资源未被使用,则标记当前资源被这个线程使用,并且为锁定状态,若果当前资源已经被使用,就构件一套等待和唤醒分配机制。
- 比如ReentTrantLock独占锁的实现,就是只能有一个线程执行。
- Share 共享锁。多个线程可执行:比如CountDownLatch
- 可以理解为一个构件同步器的框架,用于处理,当一个线程访问一个共享资源的时候,如果资源未被使用,则标记当前资源被这个线程使用,并且为锁定状态,若果当前资源已经被使用,就构件一套等待和唤醒分配机制。
- 其内部实现其实是一个volatile 的 int state共享变量,使用CAS来更新这个state
- Synchronized底层最新版本做的优化:无锁、偏向锁、轻量级锁、重量级锁、自旋锁(等一下)、
- https://juejin.im/post/5a43ad786fb9a0450909cb5f
Java泛型、注解、反射与动态代理
泛型
注解
反射
动态代理
Java集合
Java关键字
Android 知识
- Binder:性能方面 + 安全方面的考量
- 性能方面:只涉及到一次内存拷贝,效率较高
- 安全方面:校验对应的UID凭据
- 用户空间 - 内核空间:0-1GB-4GB
- 系统调用:copyfromuser、copytouser
- Binder驱动、mmap
- open /ioctl / mmap /dev/binder
总结自我成长
- View层面:弹幕、大图模式
- 技术层面的:插件化、阅读SDK
- 业务层面:完成书城的本地化、SDK业务的从0到1
- 日活、稳定性等等
简历层面
- 移动端复杂交互:比如播放器View(Scroller)、漫画弹幕(Span)、大图模式(ViewPager)、Banner图
- Feed流,RecyclerView的优化,怎么定位问题,联系到Facebook的Litho,淘宝的Talgram?
- 性能优化怎么做?比如怎么做滑动性能的优化,从浅到深
- Flutter做了什么,基本原理,线程模型,怎么实现的插件化
- 插件化,怎么做的,四大组件怎么处理的
- SDK业务,怎么做的,技术方案,
字节跳动面试
- http://blog.itpub.net/69952849/viewspace-2681234/
- 一道深度遍历和公共节点寻找的题
算法:
- 数组实现队列
- 如何从一百万个数里面找到最小的一百个数,考虑算法的时间复杂度和空间复杂度
- 二分查找
- 判断时间复杂度、空间复杂度
- ViewGroup遍历
- 一道两个链表的相加问题。思路+伪码实现(这里和阿里不一样,因为是牛客的平台,其实编码体验没有特别好,面试官让我用伪码来实现思路就可以了)
计算机基础
https://juejin.im/post/5e853bb9518825739a1af0c9?utm
_source=gold
_browser
_extensionhttps://juejin.im/post/5e843f676fb9a03c563c0178?utm
_source=gold
_browser
_extension
Java
- 泛型擦除,通配符的问题,主要做了extends和super的回答
- 从内存泄漏问题推倒到jvm的深层次问题,可达性分析的GC Root有什么,虚拟机栈和堆的关系,方法是怎么实现的
Android
- PluginManager、PluginClassLoader的实现
- 组件化与插件化:
Flutter
- https://zhuanlan.zhihu.com/p/58298219
大体如下:
- 数组实现队列
- gc的流程
- java软引用与弱引用区别
- java中的this编译时的原理
- final变量用反射修改
- HashMap的内部结构,给定一个key,如何找到对应的value,使用equal
- volatile
- Java线程池有什么作用
- Java动态代理
- handler机制
- android跨进程通信的方式 12.自定义控件方式 13.Canvas绘制过什么 手写功能 14.断点续传的实现 15.如何设计图片加载库 16.有看过哪些安卓的源码 View树绘制 事件分发 Activity启动 handler 17.看过哪些开源项目 eventbus volley 图片 线程池管理 插件化 资源加载 18.app 启动速度的优化做过哪些 19.fresco加载图片原理 优势是什么 20.写程序时,堆和栈有什么优化点 内存回收时机 如何判断对象可被回收 引用计数法和gc root法
- 事件分发 cancel事件一般在什么时候被触发
- touchdelagate 一个父view只能设置一个delegate,如何解决设置多个
- App整个架构了解么 24.mvvm data binding
- webview 26.fragment startactivity
- 动画的原理
- 黄油计划 vsync
- 职业规划 做些有挑战的事儿 有意义的事儿 缺乏实战场景 我: 30.设计一个离线视频下载功能
开源框架
- LeakCanary | APM
- 基本原理,注册ActivityLifeCallback,onDestroy的时候watch,WeakReference之后Runtime.GC, GC之后去看ReferenceQueue里面是否存在这个引用,从而判断,
- 调用HaHa库分析这个引用链
- 然后DisplayLeakService来构造Notification
- AMS
- Resource
- View
- 写各种不同方式的单例
- 算法:合并两个有序数组
- 红黑树的时间复杂度- 二叉树的时间复杂度
面试-手淘直播
- String类的理解
- HashMap的扩容和实现
- ConCurrentHashMap的实现
- AtomicInteger的实现
- ReentrantLock VS Synchronized
- 手写单例
- 手写生产者-消费者
- 线程池原理
- == 和 equals原理
- 手写AIDL & Messenger
- 反射、注解、泛型、字节码增强
- 序列化 https://blog.csdn.net/wo2huoa/article/details/104213225
- Concurrent HashMap
- 不同的单例写法
- 二分查找实现
- 链表交点
- https通信过程:
- https://blog.csdn.net/xiaoming100001/article/details/81109617?depth
_1-utm
_source=distribute.pc
_relevant.none-task-blog-BlogCommendFromBaidu-1&utm
_source=distribute.pc
_relevant.none-task-blog-BlogCommendFromBaidu-1 - 对称加密,加密解密秘钥只有一个,比如DES,AES
- 非对称加密,秘钥成对出现,公钥加密私钥解,私钥加密公钥解: RSA
- 通信过程
- 请求https链接
- 服务端返回证书(公钥)
- 客户端本地产生随机(对称)秘钥
- 使用公钥对对称秘钥加密
- 发送加密后的对称秘钥给服务端
- 服务端用对称秘钥加密的密文通信
- 通信过程
- https://blog.csdn.net/xiaoming100001/article/details/81109617?depth
- Tcp close wait vs time wait:
- Tcp是一种面向点对点的通信协议,需要建立可靠信道
- 三次握手,保证双方接收发送都正常
- C - S: 发送F 确认 C 发送正常, S接收正常
- S - C : 发送Ack,确认S接收正常,
- C - S: 确认S发送正常, C接收正常
- 红黑树的时间复杂度:
- https://zhuanlan.zhihu.com/p/72505589
- 阅读SDK的基本架构
计算机网络
- http & https
- 签名算法及证书校验
算法类
- 高频算法题
- LRU算法实现
- 二叉树问题、二叉搜索树、
- ASM: 自定义Transform task
- ClassReader、ClassVisitor、ClassWriter
面试大纲
Java基础
- String, StringBuilder、StringBuffer的区别
- String不可变,用final byte[] value 实现;另外俩非final
- StringBuilder线程不安全
- StringBuffer线程安全 (相关的方法加了锁), String也是线程安全的,final特性:
- int 和 Integer的区别
- Int是8中基本类型之一,Integer则是其对应的包装类型
- 注意 == 左右的,自动装箱与拆箱,Integer == integer比的是引用,integer == int拆箱为值比较
- 另外要注意int的值缓存[-128 ,127]
- 当给一个Integer引用赋值的时候本质是在调用其ValueOf: 注意并不是每次都new一个新对象出来
public static Integer valueOf(int i) \\{
if (i >= IntegerCache.low && i <= IntegerCache.high)
```
return IntegerCache.cache[i
+ (-IntegerCache.low)];
```
return new Integer(i);
\}
== 、 equals、hashcode之间的区别与联系
- 如果两个对象a.equals(b); 则二者hashcode必须相同,反之则不一定
- == 是个操作符,如果是基本类型的话只能用==来比较值
- Equals是个方法,默认实现也是通过==来比较内存地址,覆写的话,比如string,可能比较的是char value
抽象类和接口的异同
浅拷贝与深拷贝:除了值之外,引用是否指向同一个内存
- 序列化是深拷贝
- Clone方法是浅拷贝
Java反射
- 指的是能够动态获取Java运行时类信息、或者动态调用相关方法的能力或者机制
- 使用
- 动态代理:在程序运行时生成代理类,而非手动提前生成
- 使用ASM等工具编辑字节码
- 实现InvokeHandler
HashMap的数据结构
- 使用数组
+ 链表 | 红黑树的结构 - put过程
- 先对key的hashcode进行hash,得到数组下标index
- 如果当前数组为空,则进行初始化为16
- 如果hash计算后没有碰撞,直接放到对应数组里
- 如果hash计算后有碰撞且节点已存在,就替换掉
- 如果hash计算后有碰撞且是数,就挂载到数上
- 如果hash计算后有碰撞且是个链表,则挂到链表尾节点,发现如果大于8,则转化为红黑树
- 完成put后,查看是否需要resize()16
* 0.75f
- get过程,对比hash,对比key
- HashMap和HashTable的区别
- 线程安全性
- HashMap对key进行了二次hash,而HashTable直接用hashcode
- HashMap键和值都可以为null,HashTable不行
- 扩容机制不一样,一个是二倍,一个是二倍
+ 1;
- 这里注意,HashMap的初始化容量设置会校正为2的幂
- 使用数组
ConCurrentHashMap 对比 HashMap
- ConCurrentHashMap的键值对不能为null
- 线程安全
- 之前是segment数组,通过ReentrantLock
- Jdk1.8 是数组
+ 链表 |红黑树的方式,是通过Synchronized关键字和CAS操作来实现的 (get是没有枷锁的) - https://www.imooc.com/read/47/article/858
- https://www.imooc.com/read/47/article/850
ArrayMap 对比 HashMap
Java内存结构与类加载
- 堆:JVM启动的时候创建,存放大量的实例对象,GC也主要是这块区域,线程共享
- 方法区:类信息、静态变量、常量、编译产生的符号引用字面值等
- 虚拟机栈:存储栈帧,栈帧上面主要包括:局部变量表、操作数栈、返回地址、动态链接
- 本地方法栈:Native
- 程序计数器:保存线程执行信息 (唯一一个不发生OOM的地方)
JVM 的类加载机制
- 双亲委派机制
- 加载、链接 (验证、准备、解析)初始化
JVM与GC
- 如何判断对象死去:使用可达性分析
- 以GCRoots为节点,遍历,如果不可达,则认为对象
- 虚拟机栈中引用的对象
- 方法区中的静态变量引用的对象
- 方法区中的常量应用的对象;
- JNI引用的对象;
- 垃圾回收算法有哪些
- 标记-清除算法
- 复制算法 (一次性清除)
- 标记-压缩算法
- 分代收集策略
- 新生代采用复制算法
- 老年代采用标记-压缩/清除算法
- 并发GC 与 串行GC
锁与并发
- 线程死锁
- 什么是线程死锁:两个或者两个以上的线程同时竞争资源或者通信造成阻塞,并且无外力就永远阻塞
- 产生条件:
- 互斥
- 请求与保持
- 不可剥夺
- 循环等待
- 如何避免:打破条件
- Synchronized 和 Lock的区别
- Synchronized本身是JVM层面支持,底层通过monitor
- Lock是JDK提供的API
- Synchronized隐式的加锁与释放锁,Lock需要手动加锁释放锁
- Synchronized不可中断,Lock可中断
- 加锁是否公平
- AtomicInteger的实现原理
- 使用底层CAS指令,通过对比当前值与预期值是否一致,如果一致,则进行更改,
- 循环开销、ABA问题,只能保证自身原子性
- 线程死锁
线程和线程池
- 线程的状态:new、runnable、blocked、waited、time
_waiting、terminated - 线程状态切换
- 为什么要使用线程池
- 池化资源:避免创建大量重复对象,有效利用
- 便于管理
- 如何使用线程池
- 使用ThreadPoolExecutor
- 线程池核心参数:CPU密集型,CPU
+ 1, IO密集型 CPU
* 2- 核心线程数
- 最大线程数
- 等待队列
- 拒绝策略
- 存活时间
- 线程的状态:new、runnable、blocked、waited、time
ThreadLocal
- 其实用ThreadLocalMap用的当前Thread的
- 风险是会引起内存泄露
- ThreadLocalMap中Entry的Key是个弱引用,GC的时候如果无相关应用就会被回收,但value还在,可以手动调用remove方法
AQS
- 构建锁和同步器的一套框架
- 比如独占锁,ReentrantLock
- 共享锁CountDownLatch
- 也可以自定义AQS
- 底层是持有一个volatile的state
- 通过CAS操作
- 构建锁和同步器的一套框架
CountDownLatch的使用
- 底层使用AQS
+ 自旋CAS实现 (tryRelease) - CountDownLatch(2); countDownLatch.countDown(); countDownLatch.await() (阻塞直到执行完返回);
- 底层使用AQS
ReentrantLock 源码分析
- lock、boolean tryLock (注意二者的区别)、lockInterruptibly、unlock、newCondition
- 底层是用Sync (AQS)实现 - FairSync NoFairSync
- tryAcquire- 判断state = 0;利用CAS持有锁并标记
- 否则判断持有锁的线程是不是当前线程、锁重入
- 否则就return false,进入同步队列
- tryLock就是调用sync.nofairTryAcquire(1);
- 公平锁的加锁,还需要判断当前线程是不是同步队列的第一个节点
Android 基础
Activity 生命周期
Activity LaunchMode & taskAffinity
Android IPC机制对比
- Android多进程方案的弊端:
- Application创建多次
- 静态成员和单例都失效
- 线程同步机制不行了
- Sp可靠性下降
- Serilizable vsparcelable
- Messenger底层实现是AIDL
- Android多进程方案的弊端:
自定义View
- View的绘制相关API
- View的测量体系
- View的事件传递体系
- View的动画体系
WMS
PMS
AMS
理解Service 、 BroadCastReceiver、ContentProvider
- LocalBroadCastReceiver
- IntentService
- HandlerThread
Android中的消息机制,理解Handler
Android中的多线程编程
- AsyncTask
- IntentService
- HandlerThread
Android中的Bitmap
Activity的状态保存
Activity 与 Fragment的区别
- Activity与Fragment通信
- assert raw的区别
- ASM的使用
- gradle打包
- 如何监听帧率
Android打包过程、签名校验过程
- https://cloud.tencent.com/developer/article/1132728
- 非对称加密:公钥、私钥
- RSA算法
- 数字摘要算法:MD5、SHA-1
- signApk
- META-INF
- 打开apk,(zip)遍历所有文件,如果是目录就跳过,为每一个文件利用消息摘要算法SHA,提取出该文件的摘要然后进行BASE64编码,然后写入MANIFEST.MF文件,一堆一堆,NAME是文件路径
- 对这个MANIFEST.MF文件计算整体SHA-1值,经过BASE64编码后,记录在CERT.SF中
- 然后逐条对MANIFEST.MF中的每个块做同样的操作,记录到CERT.SF文件中
- 用私钥对CERT.SF文件计算出签名,然后将签名及公钥信息的数字证书一同写入CERT.RSA
- 利用JarVerfier
ANR定位与修正
Service 与 Activity通信
什么情况下导致OOM
Android 动画框架实现原理
Android为每个应用程序分配的内存大小
- android:largeHeap
Activity、Window、View
动态代理
Fragment的特点
- Fragment生命周期
- onAttach、onCreate、onCreateVIew、onViewCreated、onActivityCreated、onPause、onDestroyView,onDetach
invalidate和postinvalidate
View的刷新机制
网络下载一个图片,注意什么
项目中常用设计模式
bitmap对象的理解
service生命周期
asset 和 raw文件在
fragment跟activity生命周期关联
viewpager源码
ANR监控
代理模式
+ 装饰模式AndRes原理
线程池
So的插件化
APM:自定义Gradle插件
+ ASM- 利用Gradle的Trasnform 修改class
- classVistor
- MethodVisitor
LRU cache
- LinkedHashMap:数组
+ 双向链表
- LinkedHashMap:数组
UI系统
- Choreographer主要负责获取VSYNC同步信号并同意调度UI的绘制任务
- 创建FrameHandler(主线程Looper)
- FrameDisplayEventReceiver
- doFrame
- AnimationHandler.addAnimationFrameCallback()
- 构造Choreographer
- AnimationHandler.addAnimationFrameCallback()
- performTraversals();
- Performmeasure
- Performlayout
- Performdraw
- MeasureSpec: 32位,高2位测量模式、低测量大小
- 硬件加速:硬件加速的主要原理,就是通过底层软件代码,将CPU不擅长的图形计算转换成GPU专用指令,由GPU完成。
- DisplayList是一个基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应Canvas的drawXxx()方法(如下图)。
- 信息传递流程:Canvas(Java API) —> OpenGL(C/C
+
+ Lib) —> 驱动程序 —> GPU。 - 在Android 4.1及以上版本,DisplayList支持属性,如果View的一些属性发生变化(比如Scale、Alpha、Translate),只需把属性更新给GPU,不需要生成新的DisplayList。
- updateDisplayList
- Choreographer主要负责获取VSYNC同步信号并同意调度UI的绘制任务
Android进阶
- 插件化
- 组件化
- APP架构
- 性能优化
- 内存问题
- 内存泄露
- 内存抖动问题
- 大图加载问题
- 编码取样裁剪
- 有效缓存
- 卡顿问题
- 布局优化
- 绘制优化
- 框架优化
- 主线程UI方法耗时的统计
- 启动速度优化
- 方法耗时的统计
- 线程优化
- 包体大小优化
- 功能的插件化
- 资源的混淆
- 内存问题
开源框架解析
- LeakCannary
- EventBus:作为备考
- OkHttp:重点研究OKHTTP相关源码
- Glide:生命周期的集成,结果图片的缓存
- 一些APM框架
计算机网络基础
- Http 1.0 VS Http 2.0
- http 与 https
- Tcp 三次握手与挥手
- get方法与post方法的区别
算法与数据结构
- 不同方式实现的二分查找
- 链表反转
- 树的遍历
- 深度遍历
- 广度遍历
- 动态规划
- 快速排序
- 找第N大的数
参考