面试:第十三章:中高级程序员面试

这个我提出了四种方案,好像都没有达到要求,没有办法保证一秒溢出,个人认为应该是内存分配方向可以考虑。

第一种:改配置,调小堆的大小,循环创建对象

第二种:杀死GC线程不让它回收,循环创建对象具有强引用

第三种:计算一个整数的阶乘,计算的整数很大、很大的时候,使用递归算法

第四种:同一时间,多线程创建强引用对象

 

事物的属性和隔离级别以及可能出现的问题?

事务的基本属性(ACID)

  • 原子性(Atomicity),在一个事务中的所有操作,相当于一个原子操作,要么全部成功,要么全部失败。
  • 一致性(Consistency),就是在事务执行前后,对于事务本身的用意而言,数据库中的数据是保持一致的,数据库的一致性是建立在原子性的基础之上的,更多的由编码的程序员保证,最经典的案例是A,B帐号之间的转账。
  • 隔离性(Isolation),事务的隔离性是指事务和事务之间的数据可见性,这也是本文需要详细介绍的地方。数据库定义了各种隔离级别,以在并发性和数据完整性中权衡。
  • 持久性(Durability),事务完成以后,所有的数据都将持久到数据库中,不会因为其他原因而丢失。

如果不对事务进行并发控制,可能会产生四种异常情况:

  • 幻读(phantom read):一个事务第二次查询出现第一次没有的结果,说明别的事务已经插入一些数据。注意这是在同一个事务里面的查询
  • 非重复读(nonrepeatable read):一个事务重复读两次得到不同结果,说明读取操作结果是不可重复的。
  • 脏读(dirty read):一个事务读取到另一个事务没有提交的修改,就是当另一个事务它还没有提交修改一个事务就读取到了修改。
  • 丢失修改(lost update):并发写入造成其中一些修改丢失。

为了解决并发控制可能产生的异常问题,数据库定义了四种事务的隔离级别,SQL标准定义了4类隔离级别(由低到高):

  • 读未提交(read uncommitted):别的事务可以读取到未提交改变。会出现的问题:一个事务在对数据进行修改但还未提交时,另一个事务读取到了修改后的数据,但因为某些原因修改数据的事务回滚了,出现了脏读;
  • 读已提交(read committed):只能读取已经提交的数据。会出现的问题:一个事务在查询数据时另一个修改数据的事务刚好提交,再次查询时,两次查询结果不一样,出现虚读
  • 可重复读(repeatable read):同一个事务先后查询结果一样,Mysql InnoDB默认实现可重复读级别。会出现的问题:在一个事务的两次查询中数据笔数不一致,例如:一个事务查询一次数据,在此时另一个事务插入了几条数据,当再次查询时出现了几条之前没有的数据,产生幻读;
  • 可串行化(Serializable):事务完全串行化的执行,隔离级别最高,执行效率最低。相当于把数据库在一个事务执行的时候完全锁住,等它执行完才能执行下一个。这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题

事物的传播属性和Spring事物的七种传播行为?

事物的传播属性

  • Required(需要):当前方法必须要求开启事务,如果当前线程不存在事务,则开启新的事务,如果当前线程已经存在事务,就加入到当前事务。
  • RequiresNew(需要新的):当前方法必须要求开启新的事务,如果当前线程已经存在事务上下文,就暂停当前事务,等到新事务结束之后,再继续恢复之前的事务。
  • Mandatory(强制必须):当前方法必须要求事务,如果当前线程不存在事务,就抛出异常,如果存在,就加入到事务里。
  • Supports(支持):当前方法支持事务,如果当前线程存在事务,就加入到事务中去,如果不存在,不做任何操作。
  • NotSupported(不支持):当前方法不支持事务,如果当前线程存在事务,就挂起当前事务,执行完当前方法,恢复事务。
  • Never(不用):当前方法不支持事务,如果当前线程存在事务,则抛出异常。这种用的比较少。

 

Dubbo底层实现原理和底层协议?

主要有五个角色/核心组件,分为是Container(容器)、Provider(服务的提供方)、Registry(注册中心)、Consumer(服务的消费方)、Monitor(监控中心)。

容器:主要负责启动、加载、运行服务提供者

注册中心:注册中心只负责地址的注册和查找

监控中心:监控中心负责统计各服务调用次数、调用时间

分布式框架的核心是RPC框架,RPC框架的核心是RPC协议,dubbo 支持的RPC协议,缺省协议、采用了单一长连接和NIO异步通讯、传输数据量小、使用线程池并发处理请求,能减少握手和加大并发效率

负载均衡算法?

  • HTTP重定向负载均衡:用户的一项操作需要发起两次HTTP请求,一次向调度服务器发送请求,请求首先被集群调度者截获;调度者根据某种分配策略,选择一台服务器,将选中的服务器的IP地址封装在HTTP响应消息头部的Location字段中,并将响应消息的状态码设为302,最后将这个响应消息返回给浏览器。第二次向后端服务器发送请求,当浏览器收到响应消息后,解析Location字段,并向该URL发起请求,然后指定的服务器处理该用户的请求,获取处理结果。 最后将结果返回给用户。
  • DNS域名解析负载均衡:我们通过域名访问网站之前,首先需要将域名解析成IP地址,这个工作是由DNS完成的。也就是域名服务器。它会帮我们把域名解析成IP地址并返回给我们。我们收到IP之后才会向该IP发起请求。如果一个域名指向了多个IP地址,那么每次进行域名解析时,DNS只要选一个IP返回给用户,就能够实现服务器集群的负载均衡。 
  • 反向代理负载均衡:请求都首先要经过反向代理服务器,服务器根据用户的请求要么直接将结果返回给用户,要么将请求交给后端服务器处理,再返回给用户。反向代理服务器就可以充当服务器集群的调度者,它可以根据当前后端服务器的负载情况,将请求转发给一台合适的服务器,并将处理结果返回给用户。 
  • IP负载均衡
  • 数据链路层负载均衡

负载均衡算法

  • 轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载。
  • 随机法:通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。
  • 源地址哈希法:源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
  • 加权轮询法:不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
  • 加权随机法:与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
  • 最小连接数法:最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
     

Dubbo提供的负载均衡和Nginx的负载均衡

Dubbo提供的负载均衡:

  • 随机均衡:按权重设置随机概率
  • 权重轮询均衡:按公约后的权重设置轮询比例
  • 最少活跃调用数(权重):活跃数指调用前后计数差,优先调用高的,相同活跃数的随机。响应快的提供者接受越多请求,响应慢的接受越少请求
  • 一致性Hash:根据服务提供者ip设置hash环,携带相同的参数总是发送的同一个服务提供者,若服务挂了,则会基于虚拟节点平摊到其他提供者上

说明:dubbo的负载均衡是服务层面的,而Nginx的负载均衡还在http请求层面上,完全不同。

Nginx的负载均衡:

  • 轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
  • weight:指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
  • ip_hash:每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
  • fair(第三方):按后端服务器的响应时间来分配请求,响应时间短的优先分配。
  • url_hash(第三方):按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

new一个对象有哪些初始化的操作?

加载并初始化类创建对象

加载(存在继承时):先静后非,先父后子,先块后器

初始化:

  • 当使用new创建一个对象时,jvm会在堆中分配一个内存存放该对象
  • 父类成员变量和子类成员变量堆内存创建一片内存,指向值为null,先父类成员变量显式初始化(如果有的话)
  • 父类代码块(父类成员变量初始化)
  • 父类构造器
  • 子类成员变量显式初始化(如果有的话)
  • 子类代码块(子类成员变量初始化)
  • 子类构造器
     

创建对象:

1、在堆区分配对象需要的内存

  分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量

2、对所有实例变量赋默认值

  将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值

3、执行实例初始化代码

  初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法

原文地址:https://www.cnblogs.com/javawxid/p/12811899.html