1.三次握手和四次挥手
答:建立连接的过程是利用客户服务器模式,假设主机A为客户端,主机B为服务器端。
(1)TCP的三次握手过程:主机A向B发送连接请求;主机B对收到的主机A的报文段进行确认;主机A再次对主机B的确认进行确认。
(2)采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B,因而产生错误。失效的连接请求报文段是指:主机A发出的连接请求没有收到主机B的确认,于是经过一段时间后,主机A又重新向主机B发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机A第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机B,主机B以为是主机A又发起的新连接,于是主机B同意连接,并向主机A发回确认,但是此时主机A根本不会理会,主机B就一直在等待主机A发送数据,导致主机B的资源浪费。
(3)采用两次握手不行,原因就是上面说的实效的连接请求的特殊情况。
2.Android 系统启动流程分析
- 按下电源,固化在 ROM 中预定位置的 Bootloader 将会被加载到内存中
- Bootloader 初始化完软硬件环境后将 Linux 内核启动起来
- Linux 内核启动时会做设置缓存、被保护存储器、计划列表和加载驱动等一些列操作,内核启动完成后会启动 init 进程
- init 进程会初始化并启动属性服务,并且解析并执行所有 init.rc 文件
- init 通过执行特定的 init.rc 文件启动 servermanager 进程,servermanager 被启动后会向 Binder 驱动发送命令让自己成为守护进程并管理所有上下文
- init 通过解析 init.rc 文件启动 zygote 进程
- zygote 进程启动的过程会创建 DVM 并为其注册 JNI 函数,然后创建服务端 Socket、启动 system_server 进程
- 启动 system_server 进程的过程会创建 Binder 线程池使其具有 IPC 能力,然后启动 AMS 等各种系统服务
- AMS 启动 Launcher,Launcher 被启动后会将已安装应用的图标显示在界面上
3.tcp和udp的区别
TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
4.java内存模型
1.程序计数器、虚拟机栈、本地方法栈、堆、方法区。
2.按照对象存储时间的不同,堆中的内存可以划分为新生代(Young)和老年代(Old),其中新生代又被划分为 Eden 和 Survivor 区
3.不同的区域存放具有不同生命周期的对象。这样可以根据不同的区域使用不同的垃圾回收算法,从而更具有针对性,进而提高垃圾回收效率。
4.方法区主要是存储已经被 JVM 加载的类信息(版本、字段、方法、接口)、常量、静态变量、即时编译器编译后的代码和数据。
5.垃圾回收算法
1.所谓垃圾就是内存中已经没有用的对象。 既然是”垃圾回收",那就必须知道哪些对象是垃圾。Java 虚拟机中使用一种叫作"可达性分析”的算法来决定对象是否可以被回收。
2.JVM 把内存中所有的对象之间的引用关系看作一张图,通过一组名为”GC Root"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,最后通过判断对象的引用链是否可达来决定对象是否可以被回收。
方法区中静态引用指向的对象。
仍处于存活状态中的线程对象。
Native 方法中 JNI 引用的对象。
标记清除算法
1.Mark 标记阶段:找到内存中的所有 GC Root 对象,只要是和 GC Root 对象直接或者间接相连则标记为灰色(也就是存活对象),否则标记为黑色(也就是垃圾对象)。
2.Sweep 清除阶段:当遍历完所有的 GC Root 之后,则将标记为垃圾的对象直接清除。
优点:实现简单,不需要将对象进行移动。
缺点:这个算法需要中断进程内其他组件的执行(stop the world),并且可能产生内存碎片,提高了垃圾回收的频率。
复制算法
优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
标记-压缩算法 (Mark-Compact)
优点:这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
缺点:所谓压缩操作,仍需要进行局部对象移动,所以一定程度上还是降低了效率。
分代收集算法
不同对象的生命周期是不一样的,因此对于不同生命周期的对象可以采用不同的收集算法,以便提高效率。
年轻代使用复制算法
年老代使用标记-压缩算法
6.双亲委派模型
所谓双亲委派模式就是,当类加载器收到加载类或资源的请求时,通常都是先委托给父类加载器加载,也就是说,只有当父类加载器找不到指定类或资源时,自身才会执行实际的类加载过程。
7.Binder机制
Android是基于Linux的发行版,Linux上的进程通信方式有管道、消息队列、共享内存、信号量、Socket。那么,为什么还要特意提供一个Binder实现进程通信呢?这要从Linux的进程空间来说明。
- 进程空间
Linux上,进程空间分为用户空间和内核空间。
用户空间:数据不可在进程间共享
内核空间:数据可在进程间共享 - 用户空间&内核空间交互
在进程内,用户空间和内核空间交互需要通过系统调用,主要是两个方法:
copy_from_user:把用户空间的数据拷贝到内核空间
copy_to_user:把内核空间的数据拷贝到用户空间 - 内存映射
通过mmap方法建立用户空间和内存空间的映射关系,从而减少数据传递需要拷贝的次数。
Binder驱动优势
高效:Binder拷贝数据只需要1次,管道、Socket、消息队列都需要2次
使用简单:采用C/S架构,实现面向对象调用方式,使用Binder跟调用本地对象一样操作简单
安全性高:Binder给每个进程分配UID/PID作为身份标识,通信时会根据UID/PID校验身份,其他通信方式没有严格的校验过程
8.AMS
1、ActivityManagerService的作用是负责管理系统中所有的Activity,想要使用它的能力,必须要获得一个它的对象。
2、ActivityManagerService是在SystemServer进程中创建的,并且运行在SystemServer进程。创建完成之后,就把它注册到ServiceManager中。
3、想要获得ActivityManagerService的能力,需要跨进程通信
9.WMS
窗口管理
WMS 是窗口的管理者,它负责窗口的启动、添加和删除 另外窗口的大小和层级也是由WMS 进行管理的。
Surface管理
窗口并不具备绘制的功能,因此每个窗口都需要有一块 Surface 来供自己绘制,为每个窗口分配 Surface 是由 WMS 来完成的。
窗口动画
窗口切换动画
输入系统的中转站
通过对窗口的触摸从而产生触摸事件, InputManagerService (IMS )会对触摸事件进行处理,它会寻找一个最合适的窗口来处理触摸反馈信息, WMS 是窗口的管理者,它作为输入系统的中转站再合适不过了。
10 LeakCanary
RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄漏。
HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄漏。如果是的话,建立导致泄漏的引用链。
引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
11 websockct和http的区别
1.支持双向通信,实时性更强,http是单向的。
2.更好的二进制支持。
3.较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有2~10字节(取决于数据包长度),客户端到服务端的的话,需要加上额外的4字节的掩码。而HTTP协议每次通信都需要携带完整的头部。
4.支持扩展。ws协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。5.websocket是有状态的,http是无状态的。
(比如支持自定义压缩算法等)
如何建立连接
WebSocket复用了HTTP的握手通道。具体指的是,客户端通过HTTP请求与WebSocket服务端协商升级协议。协议升级完成后,后续的数据交换则遵照WebSocket的协议。
12 Java的类加载机制
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
加载、连接(验证、准备、解析)、初始化
加载
1、通过一个类的全限定名来获取其定义的二进制字节流。
2、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
相对于类加载的其他阶段而言,加载阶段(准确地说,是加载阶段获取类的二进制字节流的动作)是可控性最强的阶段,因为开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。
连接
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求。为类的静态变量分配内存,并将其初始化为默认值。把类中的符号引用转换为直接引用
初始化
为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:
– 创建类的实例,也就是new的方式
– 访问某个类或接口的静态变量,或者对该静态变量赋值
– 调用类的静态方法
– 反射(如Class.forName(“com.shengsiyuan.Test”))
– 初始化某个类的子类,则其父类也会被初始化
13 Handler机制
Handler机制,主要牵涉到的类有如下四个,它们分工明确,但又相互作用
1.Message:消息
2.Hanlder:消息的发起者
3.Looper:消息的遍历者
4.MessageQueue:消息队列
Looper.prepare()
1.创建Looper对象
2.创建MessageQueue对象,并让Looper对象持有
3.让Looper对象持有当前线程
new Handler()
创建Handler对象
得到当前线程的Looper对象,并判断是否为空
让创建的Handler对象持有Looper、MessageQueu、Callback的引用
Looper.loop()
从当前线程的MessageQueue从不断取出Message,并调用其相关的回调方法。
关于Handler的问题
1.Android中,有哪些是基于Handler来实现通信的?
答:App的运行、更新UI、AsyncTask、Glide、RxJava等
2.处理Handler消息,是在哪个线程?一定是创建Handler的线程么?
答:创建Handler所使用的Looper所在的线程
3.消息是如何插入到MessageQueue中的?
答: 是根据when在MessageQueue中升序排序的,when=开机到现在的毫秒数+延时毫秒数
4.当MessageQueue没有消息时,它的next方法是阻塞的,会导致App ANR么?
答:不会导致App的ANR,是Linux的pipe机制保证的,阻塞时,线程挂起;需要时,唤醒线程
5.子线程中可以使用Toast么?
答:可以使用,但是Toast的显示是基于Handler实现的,所以需要先创建Looper,然后调用Looper.loop。
6.Looper.loop()是死循环,可以停止么?
答:可以停止,Looper提供了quit和quitSafely方法
7.Handler内存泄露怎么解决?
答: 静态内部类+弱引用 、Handler的removeCallbacksAndMessages等方法移除MessageQueue中的消息
网友评论