欢迎关注公众号“Tim在路上”
1.听说你对JVM有点研究,讲一讲JVM的内存模型吧(我说虚拟机栈,本地方法栈,程序计数器,堆,方法区)
总的有什么,生命周期,每一个
- JVM 的分区 ,线程私有,线程共享,直接内存
- 线程私有的生命周期和线程相同,线程共享的和虚拟机的生命周期相同。
- java虚拟机栈是将方法的变量,出入口参数等以栈帧的形式存入,虚拟机中只有一个堆,堆中存入的是new出的对象,而且堆是垃圾回收的主要场所。方法区主要存储类信息,常量,静态变量和即时编译器后的代码。
2.JVM的垃圾回收机制了解吗?有哪些垃圾回收算法?原理是什么(我就说出引用计数法和可达性分析法)
- JVM 的垃圾回收,主要是在jVM的堆区,采用的分区回收。
- java堆可以分为,新生代,老年代和元数据区。
- 主要的垃圾回收算法有,复制算法,标记清除算法和标记整理算法。
- 原理是 引用计数法和 GC roots 可达性分析进行的。
3.数据库了解吗?讲出两个数据库引擎?
MyISAM 和 InnoDB;
- 他们之间的区别是, InnoDB 支持事务,MyISAM 不支持事务,InnoDB 支持外键,MyISAM不支持外键;
InnoDB 支持行级锁,MyISAM 只支持表级锁,InnoDB 支持崩溃后恢复,MyISAM 不支持崩溃后恢复,其次是他们索引都是用的B+树,但是也不完全一样。
MyISAM 适合于查多更新少的情况以及存在大量整表count的情况。
4.OSI和TCP/IP都有哪些层?
OSI 的 七层网络模型,主要有 物理层,数据链路层,网络层,传输层,会话层,表示层以及应用层;
TCP/IP 网络接口层,网络层,传输层,应用层
应用层,HTTP,DNS,FTP等, 传输层主要有,TCP,UDP协议
网络层主要有,IP,ICMP,ARP,网络接口层 PPP
5.TCP/IP的运输层和网络层做了什么?起到了什么作用?
网络层主要是,通过路由选择为,为报文和分组选择合适路径,进行流量控制和差错校验;寻址;
传输层主要是,起承上启下作用,向用户提供可靠的端到端的差错和流量控制,保证报文正确传输。传输层向高层提供面向连接和面向无连接的两种服务。
IP协议是网际互联层最重要的协议,它提供的是一个不可靠、无连接的数据报传递服务
6.HTTP协议了解吗?能和我说说HTTP请求报文的结构吗?(这个问题我好像理解错题意了,GG)
HTTP 请求报文结构,主要有,请求行(请求方法,url,http版本),请求头(k-v对,把详细信息高速服务端,语言类型,长连接等),空行(请求头结束),请求报文主体(post有,发送给客户端的信息)
HTTP 响应报文结构,起始行(协议版本,返回状态码,返回信息),响应头,空行,返回内容;
7.假如我在浏览器输入一个网址,按下回车,你能告诉我用了哪些协议,发生了什么吗?(我说应用层先通过DNS协议把主机名转换成IP,然后发送HTTP请求报文,运输层TCP协议将报文分成报文段继续往网络层传输,网络层负责转发至目的主机,然后服务器巴拉巴拉的)
- 浏览器查询域名IP, 这个过程会使用 DNS 协议进行查找,先进行本地hosts和缓存的查找,进行递归查询;
- 浏览器向服务器发送HTTP请求,这个主要TCP协议,建立TCP连接时使用IP协议,OSPF协议,ip数据包在路由之间的旋转协议,ARP ,ip 转MAC地址协议,HTTP 协议
- 服务器返回 HTML 响应
- 浏览器展示 HTML
8.你能和我说说操作系统的核心内容吗?(我说了内存管理,线程进程调度,磁盘调度,CPU啥的巴拉巴拉的,说着说着面试官让我停了,说差不多可以了)
操作系统是一组管理计算机硬件和软件,同时调度资源的程序集合。
操作系统主要提供,内存分配回收,空间扩充,地址转换和存储保护。
操作系统利用虚拟存储技术,进行空间的扩充。
虚拟内存的基本思想是,每个进程有用独立的逻辑地址空间,内存被分为大小相等的多个块,称为页(Page).每个页都是一段连续的地址。
9.Spring的bean了解吗?他的生命周期说一说?(我就说出了 实例化,属性赋值,初始化,销毁)
- 根据配置文件中的信息,实例化Bean, 主要包括扫描配置文件包,加载@Service 等bean,进行配置
- 使用依赖注入填充属性,@Autowared和 @Resource
- 如果bean 实现了BeanNameAware接口,通过bean id 调用setBeanName()
- 如果bean 实现了BeanFactoryAware接口,通过传入factory自身,调用setBeanFactory方法
- 如果是实现呢BeanApplicationAware接口,通过传递Spring上下文调用set方法。
- bean如何实现和BeanPostProcessors方法,则调用,进行一些属性值性等操作
- 如果bean 实现 init-method方法调用初始化
- 调用BeanPostProcessors方法
- 调用 bean 的 destroy()方法
- 如果为bean指定了destroy方法(<bean>的destroy-method属性),那么将调用它
10.Spring的AOP了解吗?知道实现原理的吗?知道什么时候创建代理对象吗?(我说实现原理是动态代理)
Spring AOP是基于动态代理实现的,Spring AOP 支持方法和属性级别的Pointcut;
主要有两种,JDK 动态代理,一种是通过 cglib 的动态代理
可以通过开启proxy-target-class = true 使用 cglib方式
使用cglib 方式可以对没实现接口的类方法进行aop,比如 controller
11.hashmap这个东西是不是线程安全的啊?hashmap线程不安全的原因是啥,结合1.7和1.8说说?你觉得1.7当中的那个死循环是导致它不安全的原因嘛?
hashMap 不是线程安全的;
- 首先jMM内存模型,线程只能先和自己的工作内存交互,之后共享内存,会出现读写数据不一致的问题;
- 如果写线程唯一,读线程多个还会造成线程安全问题吗?
如果在集合迭代的过程中,iterator(迭代器)不知道集合发生了修改(add/remove)操作,就会报错 如何实现遍历集合的同时进行修改:让iterator知道,即用iterator自带的remove方法:iterator.remove();
1.写线程唯一、读线程不确定,没有迭代操作。使用hashmap不会存在程序不安全,多就是发生数据不一致性的问题。 2. 场景2:写线程唯一、读线程不确定,有迭代操作,此时不能使用hashmap,会存在fastfail问题 3. 场景3: 读写线程是同一个,且唯一,有迭代操作,此时注意不能通过集合方法remove或者add更改,只能通过iterator内方法来 更新。不然会存在fastfail问题。
12.那这个concurrenthashmap是线程安全的吗?它们两个之间的区别是什么?
利用CAS + synchronized 实现node节点粒度的并发
resize的时候单线程构建一个nextTable(2倍原容量)、多线程扩容 put的时候如果检测到需要插入的位置被forward节点占有,就帮助扩容、如果检测到的节点非空且不是forward节点,对节点加syn锁, 进行节点插入 get的时候不加锁,可多线程查找 remove的时候如果检测到需要删除的位置被forward节点占有,就帮助扩容、如果不是,则对节点加syn锁,进行节点删除
13.hashmap在1.8引入了红黑树,那为什么要引入红黑树?
加快检索速度,同时红黑树相比avl树,在检索的时候效率其实差不多,都是通过平衡来二分查找。但对于插入删除等操作效率提高很多。红黑树不像avl树一样追求绝对的平衡,他允许局部很少的不完全平衡,这样对于效率影响不大,但省去了很多没有必要的调平衡操作,avl树调平衡有时候代价较大,所以效率不如红黑树。
14.你能说说concurrenthashmap和hashmap的put过程吗?
HashMap put流程
1.通过hash函数计算key的hash值,调用putVal方法
2.如果hash表为空,调用resize()方法创建一个hash表
3.根据hash值索引hash表对应桶位置,判断该位置是否有hash碰撞
3.1 没有碰撞,直接插入映射入hash表
3.2 有碰撞,遍历桶中节点
3.2.1 第一个节点匹配,记录该节点
3.2.2 第一个节点没有匹配,桶中结构为红黑树结构,按照红黑树结构添加数据,记录返回值
3.2.3 第一个节点没有匹配,桶中结构是链表结构。遍历链表,找到key映射节点,记录,退出循环。
ConcurtHashMap put流程
- 计算Hash值
- 判断当前的table是否为空,如果为空则进行初始化操作。
- table不为空则根据Hash值找到对应下标的节点
下标节点为空则通过cas将新节点放入,失败进入循环 - 如果为ForwardingNode类型,则表示当前其他线程正在扩容,则进入helpTransfer()协助扩容
- 如果不为空且是普通节点,则对节点上锁,往链表或者红黑树添加。
- cas更新baseCount,并判断是否需要扩容
HashMap 的扩容过程?
1、首先建立新数组newTable、为原数组的两倍
2、将原数组hash到新数组中,hash & (newLength-1),
3、如果原数组节点只有一个头节点,则hash到新数组直接放入
4、如果原数组e是树节点,则将其split(保持顺序分裂成两个树节点TreeNode list、list过长则转化成树,不然则彻底转成node list)
5、如果是链表,则保持原数组中链表的顺序,hash到新数组中
CurcurtHashMap 进行扩容?
resize的时候单线程构建一个nextTable(2倍原容量)、多线程扩容 put的时候如果检测到需要插入的位置被forward节点占有,就帮助扩容、如果检测到的节点非空且不是forward节点,对节点加syn锁, 进行节点插入 get的时候不加锁,可多线程查找 remove的时候如果检测到需要删除的位置被forward节点占有,就帮助扩容、如果不是,则对节点加syn锁,进行节点删除
15.红黑树你能介绍下吗?它的左旋右旋是怎么样的?为什么要左旋右旋呢?红黑树便于查找元素吗?
红黑树是二茬查找树的一种,每一个节点要么是黑要么是红,父节点是黑,叶子节点是黑(nil节点),每一个红节点的两个子节点都是黑的,任意节点到每个叶子节点上都包含相同数量的黑节点,不存在一个子树高度是其他的2倍;
所以它的查找最坏时间复杂度为O(2lgN),也即整颗树刚好红黑相隔的时候;
7.哪些情况会发生OOM?
堆内存空间不足,一般是内存泄漏和数据峰值
内存泄漏的代码,HashMap 的key 是一个对象但是没实现equals方法
发生OOM 因为 栈 和 堆的空间是可以动态变化的,当栈和堆空间不足,同时系统的空间不足时,进行申请空间就会发生 OOM 异常。
即使有足够的物理内存可用,只要达到堆空间设置的大小限制,此异常仍然会被触发。
16.问问spring吧,说说ioc,了解bean的生命周期吗?再讲一下ioc容器的初始化过程吧。
spring 容器启动流程:
- 刷新容器,标记容器启动
- 将配置信息解析,注册到BeanFactory
- 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
- Bean 如果实现了BeanFactoryPostProcessor 接口,那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事,用于初始化前做点什么(例如修改属性的值,修改bean的scope为单例或者多例
- 初始化当前的事件广播器
- 初始化所有的 singleton beans(lazy-init 的除外
- 广播applicationcontext初始化完成
spring MVC 启动流程:
总体概括spring的启动过程:
1.首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
2.其 次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时 contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始 化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是 XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的 context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
3.再 次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这里是DispatcherServlet,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请 求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE 先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个 parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方 法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是 mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为 Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet 就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些 bean
11.知道beanpostprocessor嘛?
BeanPostProcessor即,Bean的后置处理器,它的作用就是在Bean的初始化方法前跟后进行拦截处理
17. JAVA中集合和数组的区别?
- 数组声明了它容纳的元素类型,而集合不声明;
- 数组长度是固定的,集合是可以改变的;
- 数组里面只有一个类型,集合没有声明泛型可以添加多个类型;
- 数组是内置数据类型,效率高
- 集合可以面向接口编程,具有封装,继承,多态的特性;
18. Redis 持久化的方式?
redis 持久化有两种方式 RDB快照, AOF 命令保存
19. OSI 七层网络模型?
- 物理层,利用传输介质,为数据链路层提供网络服务,实现比特流透明传输。
- 数据链路层,通过差错控制和流量控制,使有差错的物理线路变为无差错的数据链路。
- 网络层,通过路由算法为报文和数组选择适当的路径。
- 传输层,向用户提供可靠的端到端的差错控制,流量控制;
- 应用层,向用户提供服务。
20. 跳表是什么?
跳表就是加索引的链表。
它的插入和删除的时间复杂度是 O(log(n))
21. Spring 中的过滤器?
过滤器是请求到达Servlet 之前进入
拦截器是AOP 的一种策略,用于某个方法或者某个字段进行拦截。
过滤器是在 Servlet 规范中定义的,是由 Servlet 容器支持的;拦截器是在 Spring 容器内的,由 Spring 框架支持。
过滤器是基于函数的回调,而拦截器是基于 Java 反射机制的
过滤器可以修改 request,而拦截器则不能
过滤器需要在 servlet 容器中实现,拦截器可以适用于 JavaEE、JavaSE 等各种环境
拦截器可以调用 IOC 容器中的各种依赖,而过滤器不能
过滤器只能在请求的前后使用,而拦截器可以详细到每个方法
22. JAVA 自动装箱和拆箱
自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱
自动装箱有一个问题,那就是在一个循环中进行自动装箱操作的情况,如下面的例子就会创建多余的对象,影响程序的性能。
Integer sum = 0;
for(int i=1000; i<5000; i++){
sum+=i;
}
上面的代码sum+=i可以看成sum = sum + i,但是+这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。
23. 什么是事务?
事务就是一系列操作,他们要符合ACID,要么全成功,要么全失败;
24. 进程和线程上下文切换了什么,共享了什么?
进程活动所需要的信息成为上下文,主要包括,进程id,指向可执行文件的指针,栈,静态和动态分配变量的内存,处理器寄存器;
线程共享进程,线程的上下文,栈,状态,寄存器
进程的切换,主要是页目录切换新的地址空间,切换内核栈,切换硬件上下文
线程的切换是不需要切换地址空间,他们共享进程地址空间
25. 进程独占什么、切换时候内核做了什么?
进程在操作系统内核程序临界区中:进入临界区后,需要独占式地访问共享数据,理论上必须加锁,以防止其他并行程序进入,在解锁前不应切换到其他进程运行,以加快该共享数据的释放
操作系统内核将原进程的现场信息推入到当前进程的内核堆栈来保存它们,并更新堆栈指针。内核完成从新进程的内核栈中装入新进程的现场信息、更新当前运行进程空间指针、重设PC寄存器等相关工作之后,开始运行新的进程。
26. 为什么要有ip和mac、mac地址是如何来的?
IP地址,相信大家都很熟悉,即指使用TCP/IP协议指定给主机的32位地址。
每个以太网设备在出厂时都有一个唯一的MAC地址了
每台主机都分配唯一的IP地址了,为什么还要在网络设备(如网卡,集线器,路由器等)生产时内嵌一个唯一的MAC地址呢?主要原因有以下几点:(1)IP地址的分配是根据网络的拓朴结构,而不是根据谁制造了网络设置。若将高效的路由选择方案建立在设备制造商的基础上而不是网络所处的拓朴位置基础上,这种方案是不可行的。(2)当存在一个附加层的地址寻址时,设备更易于移动和维修。例如,如果一个以太网卡坏了,可以被更换,而无须取得一个新的IP地址。如果一个IP主机从一个网络移到另一个网络,可以给它一个新的IP地址,而无须换一个新的网卡。(3)无论是局域网,还是广域网中的计算机之间的通信,最终都表现为将数据包从某种形式的链路上的初始节点出发,从一个节点传递到另一个节点,最终传送到目的节点。数据包在这些节点之间的移动都是由ARP(Address Resolution Protocol:地址解析协议)负责将IP地址映射到MAC地址上来完成的。下面我们来通过一个例子看看IP地址和MAC地址是怎样结合来传送数据包的。
27. 数据库语句 count(*)、count(1)、count(字段)
count(1) 和 count(*) 都会统计包含null 的字段,count(字段) 不会统计为null的数据
列名为主键,count(列名)会比count(1)快
列名不为主键,count(1)会比count(列名)快
如果表多个列并且没有主键,则 count(1) 的执行效率优于 count()
如果有主键,则 select count(主键)的执行效率是最优的
如果表只有一个字段,则 select count()最优。
1.如果在开发中确实需要用到count()聚合,那么优先考虑count(),因为mysql数据库本身对于count()做了特别的优化处理。
有主键或联合主键的情况下,count()略比count(1)快一些。
没有主键的情况下count(1)比count()快一些。
如果表只有一个字段,则count(*)是最快的。
2.使用count()聚合函数后,最好不要跟where age = 1;这样的条件,会导致不走索引,降低查询效率。除非该字段已经建立了索引。使用count()聚合函数后,若有where条件,且where条件的字段未建立索引,则查询不会走索引,直接扫描了全表。
3.count(字段),非主键字段,这样的使用方式最好不要出现。因为它不会走索引.
28. group by、order by、distinct
order by 一种是通过索引取得有序数据,不需要进行任何排序操作,即可将有序数据返回客户端
另一种是通过mysql的排序算法,将存储引擎中数据排序,返回客户端。
group by 实际上也是同样的操作,与order by 相比group by 主要是多了排序之后的分组操作
- 使用松散的索引扫描group by
在没有where语句必须经过全索引扫描的时候,松散索引扫描需要读取的键值数量与分组的组数量一样多,也就是说比实际存在的键值数目要少很多。而在WHERE子句包含范围判断式或者等值表达式的时候,松散索引扫描查找满足范围条件的每个组的第1个关键字,并且再次读取尽可能最少数量的关键字
- 使用紧凑的索引扫描group by
当GROUP BY条件字段并不连续或者不是索引前缀部分的时候,MySQL Query Optimizer无法使用松散索引扫描,设置无法直接通过索引完成GROUP BY操作,因为缺失的索引键信息无法得到。但是,如果Query语句中存在一个常量值来引用缺失的索引键,则可以使用紧凑索引扫描完成GROUP BY操作,因为常量填充了搜索关键字中的“差距”,可以形成完整的索引前缀。这些索引前缀可以用于索引查找。而如果需要排序GROUP BY结果,并且能够形成索引前缀的搜索关键字,MySQL还可以避免额外的排序操作,因为使用有顺序的索引的前缀进行搜索已经按顺序检索到了所有关键字。
- 使用临时表实现GROUP BY
MySQL在进行GROUP BY操作的时候要想利用所有,必须满足GROUP BY的字段必须同时存放于同一个索引中,且该索引是一个有序索引(如Hash索引就不能满足要求)。而且,并不只是如此,是否能够利用索引来实现GROUP BY还与使用的聚合函数也有关系。
1.尽可能让MySQL可以利用索引来完成GROUP BY操作,当然最好是松散索引扫描的方式最佳。在系统允许的情况下,我们可以通过调整索引或者调整Query这两种方式来达到目的;
2.当无法使用索引完成GROUP BY的时候,由于要使用到临时表且需要filesort,所以我们必须要有足够的sort_buffer_size来供MySQL排序的时候使用,而且尽量不要进行大结果集的GROUP BY操作,因为如果超出系统设置的临时表大小的时候会出现将临时表数据copy到磁盘上面再进行操作,这时候的排序分组操作性能将是成数量级的下降;
DISTINCT实际上和GROUP BY的操作非常相似,只不过是在GROUP BY之后的每组中只取出一条记录而已。所以,DISTINCT的实现和GROUP BY的实现也基本差不多,没有太大的区别。同样可以通过松散索引扫描或者是紧凑索引扫描来实现,当然,在无法仅仅使用索引即能完成DISTINCT的时候,MySQL只能通过临时表来完成。但是,和GROUP BY有一点差别的是,DISTINCT并不需要进行排序。也就是说,在仅仅只是DISTINCT操作的Query如果无法仅仅利用索引完成操作的时候,MySQL会利用临时表来做一次数据的“缓存”,但是不会对临时表中的数据进行filesort操作。当然,如果我们在进行DISTINCT的时候还使用了GROUP BY并进行了分组,并使用了类似于MAX之类的聚合函数操作,就无法避免filesort了
29. 一条sql语句,数据库做了什么?
- 客户端发送一条查询给服务器;
- 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果。否则进入下一阶段;
- 服务器端进行SQL解析、预处理,再由优化器生成对应4的执行计划;
- MySQL根据优化器生成的执行计划,调用存储引擎的API来执行查询;
- 将结果返回给客户端。
一条sql 的更新过程
1、首先执行器会找引擎取id=1这条数据;
2、因为id是主键,所以使用树来找到一行数据。不过引擎先去内存中查找是否有这一页数据;
3、如果有则直接返回数据给执行器;如果没有就会去磁盘把数据读入到内存中,然后返回数据给执行器。
4、执行器就会执行C+10操作;
5、执行器生成新的一行数据;
6、再调用 InnoDB 引擎的写入接口,把数据更新到内存中;
7、InnoDB 引擎写入 redo log 日志,标记状态为 prepare,并且告诉执行器已经更新数据完成,可以随时提交事务;
8、执行器把此操作写入 bin log ,并且把 bin log 写入磁盘;
9、最后执行器调用引擎的提交事务接口,引擎把 redo log 的状态改 commit ,至此整个更新操作完成。
30. 缺页了怎么办,物理内存如何分配的
在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。
当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作:
1、检查要访问的虚拟地址是否合法
2、查找/分配一个物理页
3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干)
4、建立映射关系(虚拟地址到物理地址)
31. 虚拟内存和物理内存,为什么
进程开始要访问一个地址,它可能会经历下面的过程:
1、每次我要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址;
2、所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上;
3、进程需要知道哪些地址空间上的数据在物理内存上,哪些不在(可能这部分存储在磁盘上),还有在物理内存上的哪里,这就需要通过页表来记录;
4、页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话);
5、当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常;
6、缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。
32.http1.x的新特性
- Http1.1 提出了长连接,HTTP 可以在一次TCP中不断的发送请求
- Http1.1 支持先发送header,判断是否连接成功在发送body,节约带宽,post就是这样做的。
- Http1.1 支持多个host
Http 2.0 支持多路复用,同一个连接可以并发处理多个请求。
Http 2.0 支持服务端推送,在返回数据之外支持额外的推送。
Http 2。0 压缩了请求头;
33. 长连接和短连接,什么时候会出现
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好
34. 长连接是如何维持的,心跳机制,如果一直想要连接着怎么办
连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接;
通常的做法是,在服务器的程序中加入一个死循环,在循环中监测数据的变动。当发现新数据时,立即将其输出给浏览器并断开连接,浏览器在收到数据后,再次发起请求以进入下一个周期,这就是常说的长轮询(long-polling)方式
35. 静态多态和动态多态
Java实现多态有三个必要条件:继承、重写、向上转型。
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法
编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
重载和覆盖(重写)
36. 死锁是什么,死锁发生了怎么办
死锁,死锁的特点
死锁产生的原因?
- 资源竞争
产生死锁资源,这里的资源是不可剥夺资源
产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息)
- 进程间的推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁
P1 请求 R2, P2 请求 R1
死锁产生的必要条件?
- 互斥,进程对资源使用互斥
- 占有并等待,—个进程应占有至少一个资源,并等待另一个资源,而该资源为其他进程所占有。
- 非抢占,资源不能被抢占,只能完成任务后释放
- 循环等待,有一组等待进程 {P0,P1,…,Pn},P0 等待的资源为 P1 占有,P1 等待的资源为 P2 占有,……,Pn-1 等待的资源为 Pn 占有,Pn 等待的资源为 P0 占有。
四个条件必须同时成立才会出现死锁
解决死锁的方法?
- 破坏请求条件,资源一次性分配
- 破坏保持条件,只要有一个资源无法分配,就不给改进程分配资源
- 破坏不可剥夺条件,当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源
- 破坏圈等,系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反
例如:进程PA,使用资源的顺序是R1,R2;
进程PB,使用资源的顺序是R2,R1;
若采用动态分配有可能形成环路条件,造成死锁。
采用有序资源分配法:R1的编号为1,R2的编号为2;
PA:申请次序应是:R1,R2
PB:申请次序应是:R1,R2
这样就破坏了环路条件,避免了死锁的发生
避免死锁的方法?
银行家算法:首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此,状态包含两个向量Resource(系统中每种资源的总量)和Available(未分配给进程的每种资源的总量)及两个矩阵Claim(表示进程对资源的需求)和Allocation(表示当前分配给进程的资源)。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时,假设同意该请求,从而改变了系统的状态,然后确定其结果是否还处于安全状态。如果是,同意这个请求;如果不是,阻塞该进程知道同意该请求后系统状态仍然是安全的。
37. 什么叫字节流,什么叫数据报
字节就是散乱的数据 报文就是添加了标记,封装后的数据
38. 装箱类型的缓存机制?
byte,short,long,int 缓存范围是 -128~127 char 0~127 ,boolean 是全部缓存, float和double 没有缓存
当基本数据类型 和 封装数据类型 进行 == + , - , * ,/ 会将封装类型进行拆箱
int 4 字节,byte 1 字节, char 2 字节,long 8 字节,float 4 字节
39.mybatis的$和#有什么区别?
- $ 是字符串替换, # 是预编译处理
- mybatis 在处理 # 时会将# 替换为 ?,调用prepareStatement的set方法进行处理
- $ 将其直接替换为字符串
-
可以防止SQL注入
40.Spring的@Autowired、@Resource分别有什么作用?内部如何实现的?
@Resource 是按名字进行注入的, @Autowired 是按照类型进行注入的
- 为开启了注解注入,在Spring容器启动时,默认会向BeanFactory注册AutowiredAnnotationBeanPostProcessor后置处理器,也就是在Bean实例化完后,用来做依赖注入工作的
- 找到需要注入的成员
- 对成员元素进行循环注入
- 实例化需要注入的依赖bean
- 按类型找对应的bean
- 如果找到的bean大于1个,就判断其上面有没有Primary注解
- 通过依赖进行注入
@Resource
依赖注入的处理类是CommonAnnotationBeanPostProcessor
解析bean-》保存beanDefition到IOC容器-》根据beanDefition实例化-》根据BeanpostProcessor依赖注入
1.Autowired是Spring提供的,Resouce是JDK提供的。
2.Autowired通过AutowiredAnnotationBeanPostProcessor实现属性的依赖注入,Resouce通过CommonAnnotationBeanPostProcessor。实现属性的依赖注入。
3.注解方式实例化的bean,默认都是单例,没找到定义bean Scope的入口。
4.Autowired注解严格来说,只是按类型来注入的。因为如果按类型找不到依赖的bean,直接抛异常了。配置了多个类型的bean时,会返回指定@Primary注解的bean。
5.Resouce注解,当么有指定name时,是按类型注入的;指定了name,才按name。当然前提时,我还没弄明白jndiFactory是怎么样用的。
41.什么时候MinorGC,什么时候FullGC?
当 Edan 区内存不够就会触发,MinorGC
Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法去空间不足
(4)通过Minor GC后进入老年代的平均大小大于老年代的可用内存
(5)由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小
42.有哪些垃圾收集器?都是什么作用?都针对什么区?
老年代, cms 多线程标记清除算法, parallel old 多线程标记整理, serial old 单线程 标记整理;
新生代,ParNew 多线程复制算法,serial 复制算法, parallel scavenge 多线程复制算法
43.Linux的常见命令知道多少?
free
df -h
ps aux | grep
tail -f
more
cat
tar -cvf
tar -zxvf
top
44.Exception有哪些?Exception的分类?
Exception分为两类:非运行是异常和运行时异常。
java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异
RuntimeException 如:NullPointerException、ClassCastException;一个是检查异常 CheckedException,如 I/O 错误导致的 IOException、SQLException
检查异常 CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强 制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一 般包括几个方面:
- 试图在文件尾部读取数据 2. 试图打开一个错误格式的URL 3. 试图根据给定的字符串查找class对象,而这个字符串表示的类并不存在
45. java 单利实现双重校验锁
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 第一次 singleton 判断空
在同步代码块外进行判断,避免进入同步代码块,提高效率
- 第二次 singeton 判断空
如果 A 判空,准备进入代码块, 此时B拿到了时间片,进入同步代码块,并创建实例,A又获得时间片进入代码块,创建实例
- 使用 volatile 作用
禁止指令重排序,保证不会得到部分对象;
即还没创建完,另一个线程就在调用获得;
对变量的写操作一定在读操作之前;
46. Spring DI 是什么如何实现?
DI大概是这样的,根据我们要获取的BeanName,去IOC容器中找到对应的class,然后实例化出一个代理类对象,然后给这个对象的属性赋值。
- 获取bean 的name,如果是别名将其转换
- 从缓存中取得单利bean
- 缓存中没有单利bean,判断是否可以在当前BeanFactory中获取单利bean,否则委托当前容器的父容器去寻找,按名字和类型
- 没有的话,创建bean
3.1 判断是不是原型实例,如果是,则抛出创建失败异常,如果不是,下一步。
3.2 检查 BeanDefinition 是否在当前的容器中,如果不在那可能在父类容器中,所以委托父类容器查找,如果还没有,则再上一级容器...递归查找。
3.3 检查这个实例是否是为了类型检查而获取,而不是用来使用,如果是,标记这个bean已经被创建,如果不是,下一步。
3.4 根据 beanName获取 父类的BeanDefinition,并检查该对象类类型,比如不能是抽象类等。
3.5 根据 beanName获取所有该 bean依赖的 Bean集合,如果该集合有值,则遍历DI(递归调用 getBean())该 bean集合里的bean,并把bean注册给当前的bean(维护了一个 map来存放关系)。
3.6 如果3.4中获取的 BeanDefinition是单例,则根据该单例对象和 beanName和 args创建一个实例对象;否则,判断 BeanDefinition是否是原型,如果是则根据 beanName,该对象, args创建一个实例;否则拿到3.4获取的 BeanDefinition对象的生命周期 Scope,然后根据 scope来创建实例对象,参数 (beanName,bd,args)。
java 程序运行的整个过程
java 原文件通过编译器编译成.class字节码文件,字节码文件通过 JVM 虚拟机,生成机器码文件
- 代码编译
- 类加载,双亲委派避免重复加载,加载,验证,准备(准备会对一些常量进行初始化,遍历初始化为0或null),解析,初始化
- jVM 编译 生成字节码文件
- 将字符读入寄存器,再把它放入到存储器中
- main 指令将“Hello World\n” 字符串中的字节从主存复制到寄存器文件,再从寄存器文件中复制到显示设备,最终显示在屏幕上。
java hello word运行的整个过程;
- 编写好的java源文件,先通过解释器,编译成.class 的字节码文件;
- 然后再通过jvm的类加载器进行加载,加载时采用的是双亲委派机制,加载的过程为连接,验证,准备,解析,初始化;
- 在准备时 String s 会先初始化为null, 然后初始化阶段,赋值为,“hello word”;
- 当然,
cpu 占用率
cpu 占用率,不就是运行占用的时间/ 总时间
操作系统用户态和内核态
内核态就是cpu可以访问所有数据,包括外围设备,硬盘,网卡。cpu将自己从一个程序切换另一个程序。
用户态,只能访问内存,不允许访问外围设备。占用cpu内力被剥夺,cpu可以被其他程序获取。
为什么要有用户态和内核态
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级 -- 用户态 和 内核态
所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等. 而唯一可以做这些事情的就是操作系统, 所以此时程序就需要先操作系统请求以程序的名义来执行这些操作.
工作流程如下:
- 用户态程序将一些数据值放在寄存器中, 或者使用参数创建一个堆栈(stack frame), 以此表明需要操作系统提供的服务.
- 用户态程序执行陷阱指令
- CPU切换到内核态, 并跳到位于内存指定位置的指令, 这些指令是操作系统的一部分, 他们具有内存保护, 不可被用户态程序访问
- 这些指令称之为陷阱(trap)或者系统调用处理器(system call handler). 他们会读取程序放入内存的数据参数, 并执行程序请求的服务
- 系统调用完成后,操作系统会重置CPU为用户态并返回系统调用的结果
websocket了解多少?浏览器要向一个客户端WebSocket连接,需要经过哪些协议和报文?
websocket 是h5 提供的一种浏览器和服务器进行的全双工的网络通信技术,属于应用层。他是基于TCP, 复用HTTP握手通道。
Websocket 的优点是 支持双向通信,实时性更强,更好的二进制支持,可以不携带完整的数据包头。
WebSocket复用了HTTP的握手通道。具体指的是,客户端通过HTTP请求与WebSocket服务端协商升级协议。协议升级完成后,后续的数据交换则遵照WebSocket的协议。
在HTTP的请求首部添加,
Connection: Upgrade
Upgrade: websocket
服务端返回内容如下,状态代码101表示协议切换
WebSocket的每条消息可能被切分成多个数据帧。当WebSocket的接收方收到一个数据帧时,会根据FIN的值来判断,是否已经收到消息的最后一个数据帧。
FIN=1表示当前数据帧为消息的最后一个数据帧,此时接收方已经收到完整的消息,可以对消息进行处理。FIN=0,则接收方还需要继续监听接收其余的数据帧。
Java线程与Linux内核线程的映射关系
JVM线程跟内核轻量级进程有一一对应的关系。线程的调度完全交给了操作系统内核,当然jvm还保留一些策略足以影响到其内部的线程调度,举个例子,在linux下,只要一个Thread.run就会调用一个fork产生一个线程。
Java中线程的本质,其实就是操作系统中的线程,Linux下是基于pthread库实现的轻量级进程
虚拟机中的线程状态,不反应任何操作系统线程状态
HashMap 中比较的都
如果当前节点的哈希值、键和要添加的都一致
- tranisent 是做什么用的?
Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。
Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化
Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化
- ClassNotFoundException 和 NotClassDefFoundError 区别?
ClassNotFoundException是一个运行时异常。从类继承层次上来看,ClassNotFoundException是从Exception继承的,所以ClassNotFoundException是一个检查异常。
当JVM在加载一个类的时候,如果这个类在编译时是可用的,但是在运行时找不到这个类的定义的时候,JVM就会抛出一个NoClassDefFoundError错误。
当动态加载Class的时候找不到类会抛出该异常
当编译成功以后执行过程中Class找不到导致抛出该错误
当发生由于缺少jar文件,或者jar文件没有添加到classpath,或者jar的文件名发生变更会导致java.lang.NoClassDefFoundError的错误。
当类不在classpath中时,这种情况很难确切的知道,但如果在程序中打印出System.getproperty(“java.classpath”),可以得到程序实际运行的classpath
运行时明确指定你认为程序能正常运行的 -classpath 参数,如果增加之后程序能正常运行,说明原来程序的classpath被其他人覆盖了。
NoClassDefFoundError也可能由于类的静态初始化模块错误导致,当你的类执行一些静态初始化模块操作,如果初始化模块抛出异常,哪些依赖这个类的其他类会抛出NoClassDefFoundError的错误。如果你查看程序日志,会发现一些java.lang.ExceptionInInitializerError的错误日志,ExceptionInInitializerError的错误会导致java.lang.NoClassDefFoundError: Could not initialize class。
- jdk 的命令行工具 ?
jps 虚拟机进程工具,
jstack 用于生成虚拟机当前时刻的线程快照
jinfo 作用是实时地查看和调整虚拟机各项参数
jstat(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据
jstack 寻找java 的死锁
- 用什么查询工具对 java 程序来判断是死锁?
jstack
- utf-8是怎么做的?
英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码;
大写的字母A是65(二进制01000001)
简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号。
每一个符号都给予一个独一无二的编码,就是 Unicode.
UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式.
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度
- 对一个类里面有数据库连接,怎么对这个类加锁?
在Java程序运行时环境中,JVM需要对两类线程共享的数据进行协调:
1)保存在堆中的实例变量
2)保存在方法区中的类变量
- 怎么java实现多线程对资源安全访问的方法?
- wait()/notify()方法
- await()/signal()方法
- BlockingQueue阻塞队列方法
- 除了MVC 你还知道什么架构?
被广泛使用的MVVM模式,还有MVP模式, 设计模式;
- 你一般用什么设计模式?
工厂,观察者, - 如果对一个类内的资源实现不同的操作访问,使用什么设计模式?
策略模式定义
策略模式(Strategy Pattern),将各种算法封装到具体的类中,作为一个抽象策略类的子类,使得它们可以互换。客户端可以自行决定使用哪种算法。
- CAP,ACID,两阶段提交协议,分布式事务介绍一下?
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
- 一个资源备份三台机器,如何对其进行增删操作,设计一下;
- 多线程求出1万一下的所有素数?
- 统计一个文件里面单词的数量?
- 类加载过程?
- redis 的持久化?
- redis 的机器怎么实现的?
- 程序遇见问题你一般怎么解决的?
- 运行时多态说下,举个例子?
- 求函数的极值怎么求?
- 统计一个文件下,迭代的 java 源代码的行数
find / -name *.java | wc -l
遍历此区间的int,塞进数组或集合。
然后循环套进x^2+2中,得出的结果塞进新的数组或集合。
求出这个新数组或集合的最值即可
java 的 HelloWorld 程序的整个过程;
java 原文件通过编译器编译成.class字节码文件,字节码文件通过 JVM 虚拟机,生成机器码文件
代码编译 ,词法语法语义分析,将java 原代码编译成字节码文件;
类加载机制,采用双亲委派避免重复加载(类名+类加载器),加载,验证,准备(准备会对一些常量进行初始化,遍历初始化为0或null),解析,初始化
String s = "hellowold";在jvm内存中栈和本地线程栈是线程私有的,会在线程中创建一个main方法的栈帧,里面s存放在其中的字符变量表中,然后“helloword”是一个字符串常量,在jvm中会存放在方法区中的字符常量池中的,在栈中 s 变量, 去指向它,如果 这个 new String("helloword"), 先指向堆空间,对空间再去指向 方法区常量池中。方法区还存放对象的元数据。
System.out.println(s):系统加载System.class字节码文件到方法区,并且系统会默认在堆区创建System.out、System.in、System.err三个对象。
字符串在被输出时会自动调用toString()方法。JVM 字节码执行引擎 生成机器节码文件,执行系统找到Main方法为其分配cpu进行执行;
cpu的执行分为取值,译码,执行
操作系统开始执行指令失败,缺中断发送;
操作系统分配一页内存,将代码从磁盘读入,继续执行
程序执行系统调用,在文件描述符中写一字符串
操作系统检查字符串的位置是否正确
操作系统找到字符串被送往的设备
设备是一个伪终端,又一个进程控制
操作系统将字符串送给该进程
该进程告诉窗口系统它要显示字符串
窗口系统确定这是一个合法的操作,然后将字符串转成像素
视频硬件将成像素表示转换成一组模拟信号控制显示器在(重画屏幕)
显示器发射电子束,你在屏幕上看到“hello world”
jstack 堆栈跟踪工具,jps 进程状态,javap -v 字节码反编译
通常设置成跟最大堆内存一样,减少GC
jvm 参数,-Xmx 最大堆内存,-Xms 最小堆内存
-Xmn 年轻代大小 -Xss 最大栈空间
-XX:MetaspaceSize
缓存一致性 64k
重量级总线锁
MESI, Share modifiy exculsive invalid
S S M S E I S S
嗅探到其他cpu 缓存对该值操作的状态
- 线程对共享变量的可见性?
第一个条件就是不能是自增自减等操作,上文已经提到volatile不保证原子性
两个变量值相互约束,也不能使用volatile
- 如何控制线程的执行顺序?
通过 join 方法,保证多线程顺序。join让主线程等待子线程结束继续执行。
join 里面就是调用 wait 方法,让主线程(Main)等待,直到thread1 执行完,控制多线程的顺序性。
ExecutorService pool = Executors.newSingleThreadExcutor();
他里面就是一个队列,只有一个线程,也会按顺序执行。
- JMM 可见性,原子性,有序性
缓存一致性
线程之间如何通信? 线程之间如何同步?
消息通信 wait notifiy
java 通信是对程序员隐式,共享内存
分布式锁
- 数据库方式,创建唯一约束, 可重入 ,id
- zookeeper ,数据结构,树型结构
- redis setnx 为key 不存在设置值
修改值传递,需要通过反射来进行互转
integer 在 -128 到 127 有内存缓存
解决方案 主动 new 来避免 intergerCache缓存
java 使用的是内核线程模型;
线程在线程池中就是一直被阻塞着,保存线程;
如果队列中没有任务时,核心线程会一直阻塞在获取任务的方法,直到返回任务。
1. 讲讲你们项目的启动过程
首先,我们项目是ssm框架。
- 创建一个servletContext 容器,将我们 Rootconfig保存到容器中;
- 创建一个contextLoaderListener, 注册到 servletContext中;
- 创建一个空的子容器,将webConfig,保存到中,setParent(servletContext),将servletContext作为父容器;
- 创建一个dispatcherServlet,并将其注册到子容器中
- contextLoaderListener,和 dispatcherServlet会进行刷新容器,将bean加载到我们容器中;
bean刷新过程
- 刷新容器,标记容器启动
- 获取子类的BeanFactory
- 对BeanFactory进行属性填充
- 调用BeanFactoryPostProcessor 进行扩展修改属性
- 调用BeanPostProcessor 修改bean创建实例
- 创建事件广播
- 刷新,广播
分布式锁需要考虑什么因素?
分布式锁可以通过 zookeeper , redis , mysql 来实现
synchronized , lock 都是对线程的加锁,不同进程加锁,需要通过1.外边存储状态,2. 全局唯一标识,3. 至少两种状态
如何提升系统的QPS(每秒查询率)和吞吐量
- 如果我们系统单节点,我们可以通过多节点+负载均衡,nginx,tomcat
- 数据库,可以增加缓存,系统拆分,异步MQ
- 应用层 CDN:依靠网络中的各个节点,就近发放静态资源
BIO, NIO,AIO 总结
IO 就是读取的文件是存储在磁盘上的,我们的目的是把它读取到内存中。可以把这个步骤简化成把数据从硬件(硬盘)中读取到用户空间中。
BIO 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成.
BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在while(true) 循环中服务端会调用 accept() 方法等待接收客户端的连接的方式监听请求.
NIO 它支持面向缓冲的,基于通道的I/O操作方法。
AIO 异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
操作系统的运行机制和体系结构
当线程陷入内核态就可以执行特权指令和非特权指令,在用户态只能执行非特权指令。
RPC 和 RestFul 的理解?
RPC 是远程过程调用,像本地方法一样调用服务器方法。
RPC 分为同步和异步
同步RPC 例如webService, RMI
异步RPC 是java实现版JMS MQ,kafka
主要是通过在客户端和服务器之间建立TCP连接,远程过程调用的所有交换的数据都在这个连接里传输。
当A服务器上的应用发起远程过程调用时,方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式,也就是序列化(Serialize)或编组(marshal),通过寻址和传输将序列化的二进制发送给B服务器。
REST 是一种架构分隔,通过HTTP协议,以URI对网络资源进行唯一标识,响应端根据请求端的不同需求,通过无状态通信。
无状态通信,是指服务端(响应端)不保存任何与特定HTTP请求相关的资源,应用状态必须由请求方在请求过程中提供
RPC 框架
1)服务提供者(Server)对外提供后台服务,将自己的服务信息,注册到注册中心
2)注册中心(Registry)用于服务端注册远程服务以及客户端发现服务。
3)服务消费者(Client) 从注册中心获取远程服务的注册信息,然后进行远程过程调用。
避免多线程竞争
- 不可变对象
- 互斥锁
- ThreadLocal 对象
- CAS;
课外喜欢读什么书籍?
- java编程思想
- java并发编程艺术
- 深入理解java虚拟机
- 重构现有代码
- elasticsearch服务器开发
- 高性能Mysql
你有什么想问?
我们部门对实习生是怎么培养的?
我们部门对后台使用的技术组件?
线程和进程的区别?
线程和进程都是对cpu工作时间段的描述
cpu在工作时会存在任务的切换。进程包括上下文切换。
线程是共享了进程的上下文环境,的更为细小的CPU时间段。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
一个程序至少有一个进程,一个进程至少有一个线程.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,同时协程比线程更轻量级,多核cpu,协程不能实现真正并行。
进程的调度算法?
FCFS 先来先服务调度算法,既可用于作业调度,也可用于进程调度。最先进入就绪队列的先进行调度。
SJF 短作业优先调度算法,后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。
时间片轮转法,每次调度时,把CPU分配给队首进程,并令其执行一个时间片。
多级反馈队列调度算法,设置多个就绪队列,并为各个队列赋予不同的优先级,优先级高的时间片越小。第一队列空闲时,调度程序才调度第二队列中的进程运行;
剥夺原则有:优先权原则、短进程优先原则、时间片原则。
linux 实时进程采用了两种调度策略,FIFO(先来先服务调度)和RR(时间片轮转调度)。
Windows 系统其调度方式比较复杂,它的处理器调度的调度单位是线程而不是进程,是基于优先级的抢占式多处理器调度
linux 下的IO复用
相同点:都使用I/O复用模型
区别:
1、支持一个进程所能打开的最大连接数不同。
select最小,单个进程所能打开的最大连接数有FD_SETSIZE宏定义。
poll使用链表无最大限制,
epoll有限制但很大,1g内存支持10w个连接。
2、FD(文件描述符)剧增后的I/O效率问题。
select、poll使用遍历模式,每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。
epoll只关注活跃的socket。因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback。socket活跃较少的情况下没有问题,但如果所有socket都很活跃的情况下,可能会有性能问题。
3、消息传递方式。
select 、poll 需要将数据从内核拷贝到用户空间。
epoll 通过内核和用户空间共享一块内存来实现。
4、触发模式不同
select(),poll()模型都是水平触发模式,信号驱动IO是边缘触发模式。
epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。
总结:
在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点:
1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。
2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善
在Java 1.4以前,只有一种IO模型,就是BIO(Blocking-IO,阻塞IO)。所以当时Java在处理高并发时性能并不好,通常使用多线程+阻塞IO来实现Web Server,然后使用线程池协助。比如Tomcat就是基于这个原理。
BIO相关的类和接口就是我们在入门Java的时候通常会了解到的InputStream、OutputStream、Reader、Writer,这些的底层原理都是BIO
阻塞IO就是应用进程通过系统调用来通知内核来准备数据,数据没有准备好久一直等待,然后拷贝完成,返回执行成功。
分页和分段的区别?
- 分页和分段都是为了管理好计算机的内存资源。
- 为了实现分段的这个技术,需要引入虚拟地址空间的概念。分段可以解决地址空间不隔离,程序运行地址不确定。
- 分页这个技术,它的虚拟地址空间仍然是连续的,但是,每一页映射后的物理地址就不一定是连续的了。正是因为有了分页的概念,程序的换入换出就可以以页为单位了。
操作系统的功能?
从资源管理的角度——五大基本功能
1.进程和线程的管理 ——进程线程的状态、控制、同步互斥、通信调度等
2.存储管理——分配/回收、地址转换、存储保护等
3.文件管理——文件目录、文件操作、磁盘空间、文件存取控制
4.设备管理——设备驱动、分配回收、缓冲技术等
5.用户接口——系统命令、编程接口
进程间的通信
每个进程有自己的地址空间,进程间的数据交换必须通过内核。
进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。
管道,半双工,只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据,管道一端的进程顺序的将数据写入缓冲区,另一端的进程则顺序的读出数据。
消息队列,消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
共享内存(share memory)
使得多个进程可以可以直接读写同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。
- 信号量
信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。
- 套接字
套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。
进程同步: 信号量,管程
线程同步: 信号量,互斥量,临界区
四种进程或线程同步互斥的控制方法
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
2、互斥量:只有拥有互斥对象的线程才具有访问资源的权限
3、信号量:为控制一个具有有限数量用户资源而设计。
线程之间的通信是没有问题的:
同步主要是临界区、互斥、信号量、事件
进程间通信是管道、内存共享、消息队列、信号量、socket
共通之处是,信号量和消息(事件)
多态
- 继承的多态
引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
常见排序算法的时间复杂度和稳定性
冒泡、选择、直接插入 排序需要两个for循环,每次只关注一个元素,平均时间复杂度为O(n2)O(n2)(一遍找元素O(n)O(n),一遍找位置O(n)O(n))
快速、归并、希尔、堆基于分治思想,log以2为底,平均时间复杂度为O(nlogn)O(nlogn)(一遍找元素O(n)O(n),一遍找位置O(logn)O(logn))
排序前后相同元素的相对位置不变,则称排序算法是稳定的;
快希选堆,不稳定,其他都稳定。
B树,B+树,红黑树本质区别
红黑树其实就是平衡树的一种,复杂的定义和规则,最后都是为了保证树的平衡性。
因为树的查找性能取决于树的高度,让树尽可能平衡,就是为了降低树的高度。
B树常用在文件系统的索引上,那为什么文件索引喜欢用B树而不是红黑树呢?
因为文件系统和数据库的索引都是存在硬盘上的,并且如果数据量大的话,不一定能一次性加载到内存。
所以一棵树都无法一次性加载进内存,又如何谈查找。所以B树的多路存储能力就出来了,可以每次加载B树的一个节点,然后一步步往下找。
是为了进一步的降低树的高度,路数越多,树的高度就越低
所以,如果实在内存中,红黑树比B树的效率更高,但涉及到磁盘操作,B树就更优了。
B+树是在B树的基础上基础上进行改造,它的数据都在叶子节点,同时叶子节点之间还加了指针形成链表*
可以方便进行范围查询。
静态链接和动态链接的区别?
- 静态链接装载是比较快
- 动态链接更节省内存,减少页面的交换。
缺点: 使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;
动态链接库,缺点是由于是运行时加载,可能会影响程序的前期执行性能。
分页和分段的区别?
主要区别表现在以下三个方面:
(1) 页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率。段则是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好地满足用户的需要。
(2) 页的大小固定且由系统决定;而段的长度却不固定,决定于用户所编写的程序。
(3) 分页的地址空间是一维的,程序员只需利用一个记忆符,即可表示一个地址;而分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址
事务是如何保证原子性的?
innodb 是通过undo 和 redo 来保证事务的原子性
每执行一条对数据产生影响的SQL(增删改),就会记录与之相反的操作到undo 日志(撤销日志),(delete 会形成 insert, update 会记录更新前), 一旦事务回滚,会执行 undo 日志,同时事务中的SQL会记录到 redo, 一旦事务提交,redo 会持久化到磁盘。
为什么使用 B+ 树?
数据库索引在数据量大的情况下都是放在磁盘上,而磁盘IO的读写效率是非常慢的,矮胖的B+树就是比较好的选择。
B+ 树的中间节点是不保存数据的,所以磁盘页可以容纳更多节点元素,更加的矮胖。
B 树的查询可以在中间节点结束, B+ 树的查询,必须查找到叶子节点,B+ 树比较稳定。
B+ 树的范围查询只需要遍历叶子节点的链表即可, B 树需要重复的中序遍历
B+ 树的叶子节点是链表结构
叶子结点本身依关键字的大小自小而大顺序链接
B+ 树允许元素重复
各种锁的应用场景?
Java线程锁总结
** 1.synchronized:**
在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronize,另外可读性非常好。
** 2.ReentrantLock:**
在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍,而ReentrantLock确还能维持常态。
高并发量情况下使用ReentrantLock。
** 3.Atomic:**
和上面的类似,不激烈情况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。但是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。因为他不能在多个Atomic之间同步。
所以,我们写同步的时候,优先考虑synchronized,如果有特殊需要,再进一步优化。ReentrantLock和Atomic如果用的不好,不仅不能提高性能,还可能带来灾难。
java 锁机制
- 乐观锁,悲观锁
- 共享锁,排它锁
- 公平锁,非公平锁
悲观锁,认为自己在使用数据的时候一定有别的线程来修改数据,在获取数据的时候会先加锁,确保数据不会被别的线程修改,具体的实现由Synchronized, 接口lock的实现类。
适用的场景:写操作较多,先加锁可以保证数据的正确性。
乐观锁,认为自己在使用数据时不会有其他线程来修改数据,在更新数据时判断有没别的线程已经更新。
实现有: CAS算法,AtomicInteger原子自增实现。
使用场景:读操作较多,不加锁可以提升性能。
阻塞到唤醒会涉及到上下文的切换。
自适应的自旋锁是假定不同的线程持有同一个锁对象的时间基本相当,竞争度趋于稳定,可以根据上一次自旋时间调整下一次自旋时间。
上下文切换,耗费资源,只有内核级线程才可以进行进程管理。
为什么线程上下文切换消耗时间?
线程栈中有程序计数器,寄存器,方法的栈帧
cpu在进行计算时计算的中间变量存储在寄存器里。
在线程切换的过程中,需要将线程的中间计算结果,存储在从寄存器中写回到内存线程PCB中保存现场,清空原有的寄存器,线程2再进行切换。线程2执行完再去唤醒线程1,需要将pcb中的中间结果写入寄存器。
- 用户态切换内核态,
- 需要保存上一个线程中现场。
一个线程的时间片只会分配到一个核上去,多个cpu核共用一套寄存器。
Synchronized 和 j.u.c.Lock 在获取锁的监视器对象,和获取state状态的区别?
Synchronied 是软件层依赖于jvm实现,Lock 使用cas + volatile是在硬件层,依赖cpu指令实现。
每一个对象都是一个监视器锁(monitor),当monitor被占用就处于锁定状态,执行monitorenter 尝试获取monitor所有权,两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现。被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。
java 对象头
在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
对象头:mark word 存储对象自身的运行时数据,类型指针,若为数组还应该记录数组长度
markword 存储hashcode,age,偏向状态,锁状态,epoch
redis setnx 的原理
setnx key value原理是,将key设置为value,
当且仅当,key不存在,若key存在,则setnx不做任何动作。
其次redis 是单进程单线程模型
释放锁,就是将rediskey 进行删除。
为了防止,最后没释放锁,可以加上过期时间。
高并发情况也会出现问题,执行时间和释放时间不同。
当前线程加的锁,可能被其他线程给释放掉。
解决思路,每一个线程设置一个唯一的时间戳或者唯一的id
在删除锁是,判断当前value是否是自己加的锁。
解决思路,可以新开线程去检测,key是否持有锁,如果有延迟超时时间10s
redission ,lock(), unlock();
进程与线程的本质区别、以及各自的使用场景。
- 进程和线程都是描述cpu时间片的一种方式
- 进程是包含了上下文环境,同时进程拥有独立的地址空间
- 一个进程可以对应多个线程,他们共享进程的全部资源,虚拟地址空间,文件描述符,信号处理等,
- 通信区别是,每一个进程有独立的地址空间,所有他们的通信 消息队列,共享内存,套接字,管道,管程,线程间通信可以共享内存通信,主要的问题是通过锁机制和信号量进行同步。
- 线程的上下文切换比进程上下文切换要快。
多进程适合于多机分布,线程适合多核分布。
业务弱相关使用进程,强相关使用线程。
进程的调度算法?
FCFS 先来先服务调度算法,既可用于作业调度,也可用于进程调度。最先进入就绪队列的先进行调度。
SJF 短作业优先调度算法,后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。
时间片轮转法,每次调度时,把CPU分配给队首进程,并令其执行一个时间片。
多级反馈队列调度算法,设置多个就绪队列,并为各个队列赋予不同的优先级,优先级高的时间片越小。第一队列空闲时,调度程序才调度第二队列中的进程运行;
剥夺原则有:优先权原则、短进程优先原则、时间片原则。
linux 实时进程采用了两种调度策略,FCFS(先来先服务调度)和RR(时间片轮转调度)。
Windows 系统其调度方式比较复杂,它的处理器调度的调度单位是线程而不是进程,是基于优先级的抢占式多处理器调度
协程的作用
- 协程的本质是用户级线程,是更轻量级的。
- 协程与线程主要区别是它将不再被内核调度,而是交给了程序自己而线程是将自己交给内核调度,
- 协程避免反复系统调用,还有进程切换造成的开销。
常见的进程同步问题
- 生产者消费者问题
- 读者和写者问题
- 哲学家就餐问题
mysql中锁,死锁解除
mysql 中锁 悲观锁 先获取锁再执行业务 select * for update
乐观锁,先执行业务,不得已去拿锁。 版本号 + 重试
mysql 常见的锁有 页级锁,表级锁,行级锁
锁算法有,record lock 记录锁,锁数据用于等号查找数据
Gap 锁,不锁记录只锁gap
next-key 锁 同时锁住记录(数据),并且锁住记录前面的Gap
mysql 中死锁
表级锁不会产生死锁.所以解决死锁主要还是针对于最常用的InnoDB.
死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。
情景1 : 锁的顺序不一致
例如两个用户同时投资,A用户金额随机分为2份,分给借款人1,2
B用户金额随机分为2份,分给借款人2,1
由于加锁的顺序不一样,死锁当然很快就出现了。
对于这个问题的改进很简单,直接把所有分配到的借款人直接一次锁住就行了。
Select * from xxx where id in (xx,xx,xx) for update
在in里面的列表值mysql是会自动从小到大排序,加锁也是一条条从小到大加的锁
情景2: 锁在查询中表中不存在改值
select * from t3 where id=22 for update;
当对存在的行进行锁的时候(主键),mysql就只有行锁。
当对未存在的行进行锁的时候(即使条件为主键),mysql是会锁住一段范围(有gap锁)
例如 现在表中有 11,12 锁住 12 到无穷大
表中 11,30 锁住 11,30
insert into t3(xx,xx) on duplicate key update `xx`='XX';
用mysql特有的语法来解决此问题。因为insert语句对于主键来说,插入的行不管有没有存在,都会只有行锁。
java中死锁的检测
jps 找到进程号 ,jstack -l
jconsole
虚拟内存的作用
- 将主存视为一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据
- 为每个进程提供了一致的地址空间,简化内存管理
- 保护了每个进程的地址空间不被其他进程破坏
VM系统将虚拟内存分割为大小固定的块,称为虚拟页(VP),来作为磁盘和主存之间的传输单元。
页表用于地址转换同时标注当前页是否在物理页中,同时也关联了具体的物理页的具体物理地址。
操作系统为每个进程提供一个独立的页表
,因而也就是独立的虚拟地址空间。
独立地址空间允许每个进程的内存映像使用相同的基本格式。
操作系统不允许用户进程修改只读代码段,共享虚拟页表,其他进程私有内存
请求分页实现虚拟内存
请求分页系统建立在基本分页系统基础之上,为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。请求分页是目前最常用的一种实现虚拟存储器的方法。
在请求分页系统中,只要求将当前需要的一部分页面装入内存,便可以启动作业运行。在作业执行过程中,当所要访问的页面不在内存时,再通过调页功能将其调入,同时还可以通过置换功能将暂时不用的页面换出到外存上,以便腾出内存空间。
文件系统 inode 和 block
inode 索引节点,用来存放文件属性的空间,通过inode 号码来找到这个空间
1.node 是存放文件属性
2.我们每创建一个文件占用一个inode(一般256字节)
3.inode 还会存放block的位置信息(指向)
4.inode 节点号相同的文件,互为硬链接文件,可以理解为是一个文件的 不同入口(一个超市,多个入口)
硬链接数量—文件入口数量
5.inode 号码在一个分区(文件系统)是唯一的。
软连接与硬链接
A是B的硬链接(A和B都是文件名),则A的目录项中的inode节点号与B的目录项中的inode节点号相同,即一个inode节点对应两个不同的文件名,两个文件名指向同一个文件,A和B对文件系统来说是完全平等的。如果删除了其中一个,对另外一个没有影响。每增加一个文件名,inode节点上的链接数增加一,每删除一个对应的文件名,inode节点上的链接数减一,直到为0,inode节点和对应的数据块被回收。
A是B的软链接(A和B都是文件名),A的目录项中的inode节点号与B的目录项中的inode节点号不相同,A和B指向的是两个不同的inode,继而指向两块不同的数据块。但是A的数据块中存放的只是B的路径名(可以根据这个找到B的目录项)。A和B之间是“主从”关系,如果B被删除了,A仍然存在(因为两个是不同的文件),但指向的是一个无效的链接。
硬链接:
a.不能对目录创建硬链接,原因有几种,最重要的是:文件系统不能存在链接环(目录创建时的".."除外,这个系统可以识别出来),存在环的后果会导致例如文件遍历等操作的混乱(du,pwd等命令的运作原理就是基于文件硬链接,顺便一提,ls -l结果的第二列也是文件的硬链接数,即inode节点的链接数)
b:不能对不同的文件系统创建硬链接,即两个文件名要在相同的文件系统下。
c:不能对不存在的文件创建硬链接,由原理即可知原因。
软链接:
a.可以对目录创建软链接,遍历操作会忽略目录的软链接。
b:可以跨文件系统
c:可以对不存在的文件创建软链接,因为放的只是一个字符串,至于这个字符串是不是对于一个实际的文件,就是另外一回事了
僵尸进程和孤儿进程
1、孤儿进程:子进程执行完毕时发现父进程已退出,子进程变成为了孤儿进程。孤儿进程后期会被系统的 init 进程接管,并 wait/waitpid 其执行状态做回收处理。对系统并无危害。
2、僵尸进程:子进程执行完毕时发现父进程未退出,会向父进程发送 SIGCHLD 信号。但父进程没有使用 wait/waitpid 或其他方式处理 SIGCHLD 信号来回收子进程,子进程变成为了对系统有害的僵尸进程。
僵尸进程无法被系统有效的回收,ps 查看时状态为 Z 的即为僵尸进程,或 top命令可直接看到 zombie 的个数。僵尸进程的父进程此时一定仍在运行,产生僵尸进程的元凶其实是他们的父进程,杀掉父进程,僵尸进程就变为了孤儿进程,便可以转交给 init 进程回收处理。
僵尸进程对系统的影响:
如果进程不调用wait()或waitpid()的话,那么保留的信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。
外部解决办法:
kill掉父进程,使僵尸进程变为孤儿进程。
内部解决办法:
1、父进程处理SIGCHLD信号,在信号处理函数中调用wait处理僵尸进程。
2、fork两次:借助一个中间进程,父进程只wait中间进程,让中间进程再fork出孙进程。孙进程结束后会变成僵尸进程,等中间进程一结束,孙进程就成孤儿进程了。孤儿进程对系统的影响:
孤儿进程并不会有什么危害,init进程会接管并处理掉它。
集线器 ,交换机 和 路由器
- 集线器工作在物理层,信号在线路中传播会进行衰减,集线器的作用就是对信号进行再生放大,从而扩大了网络的传输距离。集线器的网络叫做共享式网络,该网络的所有主机都属于同一个冲突域,即一台计算机发送消息,其它的计算机都能够收到。而且同一时刻只能够有一台计算机发送消息(为了防止冲突)。会进行分流。
- 交换机工作在数据链路层,集线器的信道利用率太低了,所以就出现了交换机。交换机很很多个端口,每个端口都能够连接一台计算机,当计算机A向计算机B发送信息时,会在内部建立起一条临时性的数据传输通道,如果有多台计算机同时通信,那么就会维护多条通道。那么可以看出交换机的每个端口就是一个冲突域,如果该端口只连接了一台计算机,那么就相当于没有冲突。
- 路由器工作在网络层,那么路由器A怎么知道把IP数据包从哪个端口送出去呢?路由器内部维护了一张路由表,它知道把IP数据报从哪个端口发出去。交换机是工作在数据链路层的,也即交换机只能转发局域网内的帧。如果网络A的主机想要发消息给网络B的主机就需要路由器了。
ip数据包常用字段
首部长度,总长度,标识(IP软件在存储器中维持一个计数器,每产生一个数据报,计数器 就加1,并将此值赋给标识字段。)标志(标志字段中的最低位记为MF(More Fragment)。MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片中的最后一个,标志字段中间的一位记为DF(Don’tFragment),意思是“不能分片”。只有当DF=0时才允许分片。)片偏移,(片偏移指出:较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。)ttl生存时间,首部校验和
ARP 缓存建立过程
每台主机都会在自己的ARP缓冲区中建立一个 ARP列表,以表示IP地址和MAC地址的对应关系。当源主机需要将一个数据包要发送到目的主机时,会首先检查自己 ARP列表中是否存在该 IP地址对应的MAC地址,如果有,就直接将数据包发送到这个MAC地址;如果没有,就向本地网段发起一个ARP请求的广播包,查询此目的主机对应的MAC地址。此ARP请求数据包里包括源主机的IP地址、硬件地址、以及目的主机的IP地址。网络中所有的主机收到这个ARP请求后,会检查数据包中的目的IP是否和自己的IP地址一致。如果不相同就忽略此数据包;如果相同,该主机首先将发送端的MAC地址和IP地址添加到自己的ARP列表中,如果ARP表中已经存在该IP的信息,则将其覆盖,然后给源主机发送一个 ARP响应数据包,告诉对方自己是它需要查找的MAC地址;源主机收到这个ARP响应数据包后,将得到的目的主机的IP地址和MAC地址添加到自己的ARP列表中,并利用此信息开始数据的传输。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。
广播,单播
ICMP 作用
是 IP协议的一个重要组成部分。
ICMPv6报文总体上被分为两种类型:差错报文和信息报文。
控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP不能纠正差错,它只是报告差错。差错处理需要由高层协议去完成。
mysql 连接查询和分组查询
MySQL Join
- 笛卡尔积
select * from t1 join t2;
- 左连接
select * from t1 left join t2 on t1.id = t2.id;
- 右连接
select * from t1 right join t2 on t1.id = t2.id;
- 内连接
select * from t1 inner join t2 on t1.id = t2.id;
- 左表独有
select * from t1 left join t2 on t1.id = t2.id where t2.id is null;
- 右表独有
select * from t1 right join t2 on t1.id = t2.id where t1.id is null;
- 全连接 union
左连接 union 右连接
- 并集去交集
左表独有 union 右表独有
- select count(*) as studentNum, groupId,sex from student group by groupId,sex;
表连接查询和子查询的区别
表关联是可以利用两个表的索引的,如果是用子查询,至少第二次查询是没有办法使用索引的。
将子查询转换为连接查询。
原因:子查询会多次运算查询,连接查询利于优化器优化,而且可以使用多线程查询各个连接子句。
drop, truncate, delete 比较
- truncate 和 delete 只删除数据不删除表的结构(定义)
- delete 语句是数据库操作语言(dml),这个操作会放到 rollback segement 中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。
- 速度,一般来说: drop> truncate > delete
- truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发trigger。
mysql 视图
视图就是一条SELECT语句执行后返回的结果集,
视图是一个虚拟表,同真实的表一样,视图包含一系列带有名称的列和行数据。但是,视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表。
视图的作用:
(1)简化用户的操作
关键信息来源于多个复杂关联表,可以创建视图提取我们需要的信息,
简化操作;
(2)对机密数据提供保护作用
不希望用户访问表中某些含敏感信息的列,比如salary
视图是否可以更新
(1)若视图的字段是来自字段表达式或常数,则不允许对此视图执行INSERT、UPDATE操作,允许执行DELETE操作;
(2)若视图的字段是来自库函数,则此视图不允许更新;
(3)若视图的定义中有GROUP BY子句或聚集函数时,则此视图不允许更新;
(4)若视图的定义中有DISTINCT任选项,则此视图不允许更新;
(5)若视图的定义中有嵌套查询,并且嵌套查询的FROM子句中涉及的表也是导出该视图的基表,则此视图不允许更新;
(6)若视图是由两个以上的基表导出的,此视图不允许更新;
(7)一个不允许更新的视图上定义的视图也不允许更新;
(8)由一个基表定义的视图,只含有基表的主键或候补键,并且视图中没有用表达式或函数定义的属性,才允许更新。
哪类视图是可以更新的?哪类视图是不可更新的?各举一例说明。
答:基本表的行列子集视图一般是可更新的。若视图的属性来自集函数、表达式,则该视图肯定是不可以更新的。
存储过程和触发器
存储过程与触发器因为在数据库中的作用不同,因为也就没什么性能可比性。
存储过程(StoredProcedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。触发器(trigger)是个特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(insert,delete,update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。 触发器可以从 DBA_TRIGGERS,USER_TRIGGERS 数据字典中查到。
事务 ACID
原子性:事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
原子性的实现原理,回滚日志(undo log),当事务回滚时能撤销所有已经成功的SQL语句。
如果事务执行失败或调用了rollback,便可以利用undo log 中信息回滚到之前状态。
一致性:事务开始前和结束后,数据库的完整性约束没有被破坏,都是合法的数据状态。
一致性:保证原子性,持久性,隔离性
隔离性:并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。
严格的隔离性,对应了事务,采用锁机制和MVCC
持久性:事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失。
innodb 作为mysql 的存储引擎,数据是存放在磁盘中的,同时innodb提供了buffer pool,作为数据库的缓冲。 当从数据库进行读数据时,会先从buffer pool 中读取,如果没有从磁盘读入放入buffer pool,
当向数据库写数据时,先写buffer pool,buffer pool 会定期刷到磁盘(刷脏)
问题是如果mysql 宕机,而此时buffer pool 中数据,没有刷到磁盘就会丢失。
redo log 记录写到 buffer pool中的操作。
如何避免数据库一致性被破坏
并发控制技术:保证了事务的隔离性,使数据库的一致性不会因为并发执行被操作
日志恢复技术:保证了事务的原子性,使数据库的一致性不会因事务或系统故障被破坏。同时使已提交的对数据库的修改不会因系统崩溃而丢失,保证了事务的持久性。
next-key 锁解决幻读(当前读)
innodb 的锁算法有三种: record lock, gap lock, next-key lock,
next-key lock 就是 record + gap 的综合
锁的类型可以划分为共享锁和排他锁
next-key 可以解决(当前读 select * for update) 的幻读问题。
innodb中的RR隔离级别是通过next-key locking是如何解决幻读问题的,就是锁住一个范围。
路由器是如何感知拥塞的?
- 路由器如何感知发生了拥塞:通过路由器输出端口的排队时延
- 路由器间的链路会按照时延来设置权值大小,时延小的权值小,在进行网络传输时会将链路进行排队
跳表的实现
跳表的实现是基于双向链表,每一个节点都有一定的概率向上生成一层节点,所以快表的节点是
跳表节点详情:(key, value, up, down, left, right)
每一层都是创建一个head, tail 做链表,查询的时候顺序遍历到尾结束或者大于结束,否则下一层
put过程:创建的时候,找到节点,key一致替换value, 否则二分之一的概率随机上一层,创建头尾,关联左右联系,上下联系,同时找前一个有上层节点的值,上一层将当前节点链接,迭代,直到随机失败
zookeeper 的应用场景有?
zookeeper的应用场景是命名服务管理,集群管理(master选举),分布式锁,配置管理,负载均衡
mysql 分库分表如何将一个表进行拆分?
方案1: 分库分表自增id解决方案,1. 创建一个表 id,stab, 所有进程去插入,会生成一个id,然后查询拿到这个自增id,用这个id作为不同表的id, 缺点是性能不好。
方案2:采用方案1的方法,也是创建一个表,只是这次一次分配一个id段,可以将表创建为id,start, step,... 直接为每一个表分配一个段,这个表使用完,才会在来进行申请。
方案3:采用雪花算法,41bit时间戳+10位机器id,12位序列号
ES 分片的个数设置
一般一个分片大小在30~50GB,可以根据数据量设置分片的个数
可以设置为节点个数的1.5~3倍
增强for 和普通的for的区别?
增强for的底层就是迭代器,所以使用增加for遍历LinkedList比较快
使用普通for遍历ArrayList比较快
Arrays.asList() 将数组转化list如何实现的?
Arrays.asList() 底层任然是数组,只是增加了适配器。
hashmap 1.8 的hash怎么做的?
hashmap 1.8 没有进行重hash, 采用原位置 + oldtable长度
socket中的编程事件有什么?
socket编程中的事件: OP_CONNECT, OP_ACCEPT
threadLocalMap 和 HashMap 的区别?
threadLocal 在每个线程内维护一个threadLocalMap, key 是threadLocal的弱引用,value是放入的值,threadLocalMap 中并没有链表,所以寻址方法是,线性开放定址法。ThreadLocal的hash算法,每新创建一个增加斐波那契数,均匀分布hash
ThreadLocal.get()的时候,发生GC之后,key是否是null?
弱引用,只要发生gc就会回收
但是, get 的时候属于强引用,不会回收
TreeSet 是如何比较对象相等的?
TreeSet 底层是TreeMap TreeMap 比较对象相等使用的是 compareTo, 底层是归并排序
Integer, Long,String 的hashCode 算法
hashcode 的大小都是int, Integer 的hashcode 是其本身,Long hashcode 本身右移32 异或变为int,String hashCode 将其以char数字的形式转31进制
不使用Synchronized 创建单利设计模式?
https://zhuanlan.zhihu.com/p/140479178
redis 的批处理命令?
redis 批量处理的指令有, pipeling/transcation/mget,mset
https://www.jianshu.com/p/75137d23ae4a
redis 采用的是典型的tcp交换,在一次round trip 中,发送一条命令,其中的开销 socket IO 上下文切换,执行,竞争,mget 只能处理string hash等
管道pipeling,意味着客户端可以在一次请求中发送多个命令,没有任何事务保证,其他命令可能在其中执行
事务, 具备原子性,multi, exec事务的所有命令会分批发送给redis实例,redis返回+QUEUED,表示命令已入列,但是不会执行任何命令。在收到EXEC命令时,一次执行本事务的所有命令。因此事务的性能略低于pipeline,但是相差不多。
说一下AOF重写的实现细节?
为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件
当前列表键list在数据库中的值就为["C", "D", "E", "F", "G"]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list "C" "D" "E" "F" "G"代替前面的6条命令。
相当于命令压缩
这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间的阻塞
Redis不希望AOF重写会造成服务器无法处理请求,所以Redis决定将AOF重写程序放到子进程(后台)里执行。子进程进行AOF重写期间,主进程可以继续处理命令请求;
子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性。
Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区
ArrayList 存放的最大值?
ArrayList 可以装下的最大数 Integer 最大值 减8
Arrays.Sort() 实现原理?
Arrays.sort() 底层 大于 286 使用 快排, 大于 47 使用优化的带前哨的pair插入排序,小于47 使用不带前哨的插入排序
那你详细说下最小生成树算法?
kruskal 算法,称为加边法,将边的权值进行排序,每次从中选择最小的满足条件的加入,加入的不能形成回路,直到所有的点都已经加入
prim 算法,加点法,每次加入代价最小的边所对应的点,并将点加入集合,以当前点集合筛选下一个最小的权值边,加入集合
为什么对象年龄达到15进入老年代?
为什么15进入老年代,主要原因是对象的分代年龄占4位,最大值也就15
那你详细说下空间分配担保策略?
空间分配担保策略,新生代使用复制算法,如果出现大量的对象在Minor gc 后任然存活,就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。老年代需要担保,老年代剩余的空间,可以容纳新生代区域
redis setnx 使用注意什么?
redis 锁 setnx 设置时加上 过期时间,可以防止一个线程拿到锁还没执行挂了,一直不释放的情况
其次还可能出现的情况时过期时间到了还没执行完,另一个线程抢占到了,这时候第一个线程执行完了,并执行del, 把b的锁删除了,可以在删除前进行判断一下防止误删,可以使用lua脚本实习判断,怎么实现续航,可以让A开一个守护线程,当时间快到期延时一下