八股文的一些总结

Volatile为什么不能保证原子性(两个线程分别执行100次自增 但是为什么值不等于200)
简单的说,修改volatile变量分为四步:
1)读取volatile变量到local
2)修改变量值
3)local值写回
4)插入内存屏障,即lock指令,让其他线程可见
这样就很容易看出来,前三步都是不安全的,取值和写回之间,不能保证没有其他线程修改。原子性需要锁来保证。
这也就是为什么,volatile只用来保证变量可见性,但不保证原子性。
volatile 变量写入并不保证线程安全,也不具备原子性。原因很简单,在执行内存屏障之前,不同 CPU 依旧可以对同一个缓存行持有,一个 CPU 对同一个缓存行的修改不能让另一个 CPU 及时感知,因此出现并发冲突。线程安全还是需要用锁来保障,锁能有效的让 CPU 在同一个时刻独占某个缓存行,执行完并释放锁后,其他CPU才能访问该缓存行。

线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符

为什么不用平衡二叉树做mysql的数据结构
搜索效率不足。一般来说,在树结构中,数据所处的深度,决定了搜索时的IO次数(MySql中将每个节点大小设置为一页大小,一次IO读取一页 / 一个节点)。如上图中搜索id = 8的数据,需要进行3次IO。当数据量到达几百万的时候,树的高度就会很恐怖。
查询不不稳定。如果查询的数据落在根节点,只需要一次IO,如果是叶子节点或者是支节点,会需要多次IO才可以。
存储的数据内容太少。没有很好利用操作系统和磁盘数据交换特性,也没有利用好磁盘IO的预读能力。因为操作系统和磁盘之间一次数据交换是以页为单位的,一页大小为 4K,即每次IO操作系统会将4K数据加载进内存。但是,在二叉树每个节点的结构只保存一个关键字,一个数据区,两个子节点的引用,并不能够填满4K的内容。幸幸苦苦做了一次的IO操作,却只加载了一个关键字。在树的高度很高,恰好又搜索的关键字位于叶子节点或者支节点的时候,取一个关键字要做很多次的IO。

关于Java垃圾回收相应的文档说明https://www.cnblogs.com/pensieve/p/13822542.html
1.垃圾标记阶段的算法 -可达性分析算法 -引用计数算法
引用计数算法:
优点:实现简单 判断效率高 回收无延迟
缺点:需要单独的字段存储计数器 增加了存储空间开销 每次引用对象需要重新计算 增加了时间开销 无法处理循环引用的问题容易内存泄漏
可达性分析算法:(有效的解决了循环引用的问题 防止内存泄漏)
GCroots,在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。

GCroots常用根对象:
1.虚拟机栈中引用的对象 例子:各个线程被调用的方法中使用到的参数、局部变量等
2.本地方法JNI引用的对象
3.静态属性引用的对象
4.被synchronized持有的对象
5.常量引用的对象
虚拟机中的对象可能达到的三种状态:
1.可触及的:从根节点开始,可以达到这个对象
2.可复活的:对象所有的引用都被释放 但是在finalize的时候复活了
3.不可触及的:对象的finalize()被调用,并且没有复活就进入了不可触及状态 因为finalize只会被调用一次
只有对象进入到不可触及的状态的时候才会被回收
GCroot的具体标记过程:
1.如果对象objA没有被GCroots引用,则进行第一次标记
2.进行筛选,判断对象是否有必要执行finalize()方法
筛选过程:
1.如果对象objA没有重写finalize()方法,或者finalize()已经被虚拟机调用过了,则被虚拟机视为没有必要执行
2.如果对象objA重写了finalize(),且还没被调用过,则会被加入到队列中F-Queue

2.垃圾清除阶段的算法 -标记清除算法 -标记整理算法 -复制算法
当成功区分出内存中存活对象和死亡对象后,GC接下来执行的任务就是回收垃圾,释放掉无用对象所占用的空间
标记清除算法执行过程:
当堆中的有效内存空间被耗尽的时候,就会停止整个程序也被成为STW,然后进行两项工作
标记:遍历GCroot所关联的对象,将被引用的对象的header头部都打上标记
清除:Collector从堆内存的头到尾进行线性遍历 如果发现某个对象的Header没有被记录为可达对象就回收内存
这里的清除并非真正的清除,而是把需要清除的对象地址保存在一个列表里.下次有新对象需要加载的时候,判断垃圾的位置空间是否足够,如果 足够则直接覆盖
标记清除算法的缺点:
1.效率不高,因为标记、清除都需要大量的运算
2.有STP 需要整个应用程序停止,用户体验差
3.清理出来的内存空间不连续
标记-压缩清除算法:
标记清除算法和标记-压缩清除算法的比较
相同点:他们第一阶段都会通过GCroot来寻找可达对象并标记
1.标记-压缩算法的最终效果相当于标记-清除算法执行完成之后,在进行一次碎片整理。
2.二者的本质差异在于标记-清除算法是一种非移动式的回收算法;标记-压缩是移动式的回收算法。
3.当我们需要给新对象分配内存是,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表的开销显然小了很多
标记整理算法的优点:
消除了内存分散的缺点
标记整理算法的缺点:
从效率上来说要低于复制算法
移动过程中也要STW
复制算法:
将能用的内存空间分为两块,每次只使用其中的一块内存,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收.把可达的对象,直接复制到另外一个区域中(内存连续排列),from区的对象就没有用了直接清除 ,其中新生代的伊甸园和幸存者from使用复制算法到to区
优点:
1.没有标记和清除的过程 开销小
2.复制过去保证空间连续性
缺点:
1.空间利用率低
综上:适合垃圾对象多,存活对象很少的场景

HTTPS过程
1.客户端向服务器发送HTTPS请求,连接到服务器
2.服务器有公钥和私钥 私钥解密 公钥加密 服务器将CA证书返回给客户端 证书包含公钥及颁发证书的机构
3.客户端验证证书的合法性 随机数算法生成对称加密的秘钥
4.公钥加密随机数算法生成的秘钥发送给服务器
5.双方通过随机数生成的秘钥进行http传输数据

进程同步的四种方法:
1.临界区:通过对多线程的串行化来访问公共资源或一段代码(单个进程内同步)
2.互斥量:协调共同对一个共享资源的单独访问设计的。互斥对象只有一个,只有拥有互斥对象的线程才具有访问资源的权限。(跨进程同步)
3.信号量:控制一个具有有限数量用户资源而设计。允许多个线程在同一时刻访问同一资源(跨进程同步)
4.事件:用来通知线程有一些事件已发生,从而进行后续操作

TCP相关的面试考题
为什么是三次握手?

两次握手会导致不好的问题例如:为了防止已经失效的连接请求报文段突然又传送到B,因而产生错误。例如A发出第一个连接请求报文段并没有丢失,而是在网络结点长时间滞留了,以至于延误到连接释放以后的某个时间段才到达B.本来这是一个早已失效的报文段.但是B收到这个失效的链接报文段后,就误以为A又发出一次新的链接请求.于是就向A发送确认报文段.确认链接.
为什么四次挥手客户端最后要TIME-WAIT状态等待2MSL?
1.为了保证可靠的断开TCP的链接,确保足够的时间让对方收到ACk包。若客户端的ACK丢失,server会在超时时间之前重传最后一个包
2.防止已失效的连接请求报文段出现在本链接中。A在发送完最后一个ACK报文段后,在经过2MSL,就可以使本连接持续的时间内锁产生的所有报文段都从网络中小时.这样就可以使下一个连接中不会出现旧连接请求报文段的情况.
注意:因为客户端丢失最后一个ACK的时候服务端会不断重新发送FIN,MSL是指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果知道2MSL都还没有收到服务端的FIN,那么就推断ACK到服务端了
什么是超时重传?什么又是快速重传?
超时重传:当超时时间到达时,发送方还未收到对端的ACK确认,就重传该数据包
快速重传:当后面的序号先到达,如接收方接收到了1、 3、 4,而2没有收到,就会立即向发送方重复发送三次ACK=2的确认请求重传。如果发送方连续收到3个相同序号的ACK,就重传该数据包。而不用等待超时
TCP怎么保证可靠传输
1.数据包效验 2.对失序的数据包重排序 3.丢弃重复的数据 4.滑动窗口来进行流量控制 5.超时重发
停止等待协议
基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后在下发下一个分组.
自动重传ARQ 连续重传ARQ:发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方对按序到达的最后一个分组发送确认
拥塞控制的四种算法:慢开始、拥塞避免、快重传、快恢复
1.慢开始:即由小到大逐渐增大发送窗口,也就是由小到大增大拥塞窗口数值,初始值cwnd为1,然后翻倍增长
2.拥塞避免:即每经过一个往返时间RTT就把发送方的cwnd+1
什么是TCP粘包?
如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况。
原因:
1.TCP是基于字节流的,TCP把这些数据块仅仅看成一连串无结构的字节流,没有边界
2.从TCP帧的接口可以看出 TCP在首部没有表示数据长度的字段。
解决办法:
1.特殊字符控制
2.在包头添加数据包的长度
发送方为什么会产生粘包?
发送数据包过小的时候,nameTCP协议默认的会启用NAgle算法,将这些较小的数据包进行合并发送
接收方为什么会粘包?
在程序中调用的读取数据函数不能急死的把缓存区中的数据拿出来,而下一个数据又到来并有一部分放入的缓冲区的末尾,等我们读取数据时就是个粘包(放数据速度>应用层拿数据的速度)
常用的状态码
1XX:接收的请求正在处理
2XX:请求正常处理
3XX:重定向状态码
400:服务器无法处理请求
500:服务器处理请求错误
403请求被拒绝 401没授权
500请求时发生错误 503服务器超负载

猜你喜欢

转载自blog.csdn.net/lirenci123/article/details/114448251