java面试提高之----我悟了
一、 微服务之熔断以及其实现原理
熔断作用是容错下游的快速失败手段,熔断是解决服务级联故障的问题;
熔断的原理
熔断本质上是做快速失败,防止级联故障引起雪崩。它的主要采用的手段是基于断路器的设计模式。
断路器有基本模式和扩展模式。
基本模式中,断路器由两个状态和一个动作组成:断路器打开状态、断路器关闭状态和跳闸动作。在断路器关闭状态下,请求过来每次都要先经由跳闸动作,由跳闸动作判断是否需要将状态改成断路器打开状态。断路器打开状态下,请求直接执行快速失败的动作,不会向后请求。
扩展模式中,断路器增加了一个半开状态,它允许有限数量的请求通过,如果执行成功,恢复到关闭状态;如果失败,则恢复到开放,然后重启定时器,一段时间后再用半开状态来尝试。扩展模式可以半自动化的进行熔断恢复,避免了基本模式中,一旦断路器被打开完全依赖人工判断是否恢复的弊端。
二、事务的隔离级别和传播机制
- 读 未提交,就是一个事务可以读取另一个未提交事务的数据。
- 读 提交,就是事务要等另一个事物提交后才能读取数据
- 重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。MySQL是这一级别。
- Serializable序列化
Serializable是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读,不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不用。
传播机制:
spring在TransactionDefinition接口中定义了七个事务传播行为:
propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
三、concurrenthashmap扩容机制(负载因子)
concurrenthashMap桶的个数默认为16。负载因子默认为0.75, 当concurrenthashMap的table长度= 原table长度n*0.75, 则进行扩容。
四、spring boot装配原理 原理.
五、jvm栈内各个元素的作用
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应一个栈帧在虚拟机栈里面从入栈到出栈的过程。在编译程序代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法表的Code属性之中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体的虚拟机实现。
- 局部变量表
局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java程序编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需要分配的局部变量表的最大容量。 - 操作数栈
操作数栈是一个后入先出的栈,同局部变量表一样,操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long和double,32位数据类型所占的栈容量为1,64位的占2个。方法执行的任何时候,操作数栈的深度都不会超过在max_stacks数据项中设定的最大值。
一个方法刚开始执行时操作数栈是空的,方法执行过程中会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈 / 入栈操作。例如执行iadd指令时,就会将最接近栈顶的两个int元素取出并相加,然后将相加的结果再入栈。操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译程序代码的时候,编译器要严格保证这一点。比如刚才的iadd指令,它取出的元素必须是int的,不能出现诸如long和float类型的变量。
3. 动态连接(这部分比较重要)
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。我们知道Class文件的常量池中存有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数,这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接,关于方法的解析与调用看这。所以要执行某个方法时,某个指令(例如invokevirtual)将常量池中的引用作为参数,而根据这个引用就可以找到真正的栈帧。
4. 方法返回地址
一个方法开始执行后只有两种方式可以退出,
正常完成出口:执行引擎遇到任意一个方法返回的字节码指令,这时候可能有返回值传递给上层的方法调用者,是否有返回值及返回值类型是由方法返回指令决定的。
异常完成出口:方法在执行过程中遇到了异常,而且这个异常没有在方法体内得到处理,不管是何种异常,只要在本方法的异常表中没有搜索到相匹配的,就会导致方法退出。异常完成出口的方式退出方法,不会给上层调用者产生任何返回值。
方法退出时总是要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来恢复它的上层方法的执行状态。一般来说,方法正常退出时调用者的PC计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值;而方法异常退出时,返回地址是要通过异常处理表来确定,栈帧中一般不会保存这部分信息。方法退出过程实际相当于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有)压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令等。
六、jvm垃圾回收机制
我就答了分代gc的过程
新建的对象会在新生代eden,如果是大对象则直接进入老生代,新生代分为eden,survive from,survive to。每次新生代gc会将eden和survive from中幸存的对象放入survive to中,然后清楚eden和survive from区,然后交换survive from和survive to中的对象,每次清理幸存的对象年龄加一,达到阈值就出放入老生代,当老生代达到阈值,触发full GC。
新生代使用复制清理法,老生代使用标记整理法。
没怎么答对,直接上链接
分布式锁之Redis实现
八、sql如何优化
我的回答:1.尽可能的命中索引,2 使用零时表暂存中间结果