高级面试题

技术特点

        JAVA基础

                                深入理解IO流原理,同步阻塞(BIO)、同步非阻塞(NIO)、异步非阻塞AIO相关原理特性以及应用场景。

                                                       

1、同步阻塞(BIO) :在jdk1.4出来之前,建立网络连接的时候采用BIO模式,需要先在服务器端启动一个ServerSocket,然后在客户端启动Socket来对服务器端进行通信,默认情况下服务器端需要对每个请求

简历一堆线程等待请求,而客户端发送请求后,先咨询服务器款是否有空闲线程响应,如果没有,则会一直等待或者遭到拒绝请求,如果有的话,客户端线程会等待请求结束后才继续执行。

 

2、同步非阻塞(NIO):本身是基于时间驱动思想来完成的,起主要想解决的同步阻塞(BIO)的大并发问题:在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时访问多个服务器

进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这要做虽然可以达到我们的要求,但同时又会代理另外一个问题。由于每创建一个线程,就要为这个线程分

配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪负重而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。

 

同步非阻塞(NIO)是基于Reactor,当啊socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流程督导缓冲区或写入操作系统。这个时候,已经不是一个连接就要对应一个处理线程

了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

 

同步非阻塞(NIO)的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连

接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

 

3、异步非阻塞(AIO):与同步非阻塞(NIO)不同,当进行读写操作时,只需直接调用API的read或write方法即可 。这种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法

的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。即可以理解为,read/write方法都是异步的,完成后都会主动调用回调函数。

 

 

4、Java对BIO、NIO、AIO的支持:

 

         Java BIO: 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情,会造成不必要的线程开销。

         Java NIO: 同步非阻塞,服务器实现模式为一个连接一个线程,即客户端发送连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

         Java AIO(NIO.2): 同步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是有OS先完成了,在通知服务器应用去启动线程进行处理

 

5、同步并阻塞(BIO)、同步非阻塞(NIO)、异步非阻塞(AIO)适用场景分析:

BIO方式适用与连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用,JDK1.4前的唯一选择,单程序直观简单易理解。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂

AIO方式适用于连接数且多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂。

 

 

 

 

                                深入理解CAS原理,对CAS应用场景有深刻的理解。

                                                         CAS(Compare and Swap),即比较再交换。

                                                          Jdk5增加了发布包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronized同步所的一种乐观锁。Jdk5之前java语言是zynchronized关键字保证同步的,这是一种独占锁,也是悲观锁

                                                         

CAS是一种无锁算法,CAS3个操作数,内存值V,旧的预期值A,要修改的值B。当预期值A和内存值V相同时,将内存值V修改B,否则什么都不做。

                                                         

                                                          说明:两个线程t1,t2,如果两个线程同时如访问同一个变量,他们会把主内存的值完全拷贝一份到自己的工作内存空间,所以t1t2线程的预期值是相同的。假设t1在与t2线程竞争中线程t1能去更新变量

                                                    的值,而其他线程都失败。(失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次发起尝试)。 T1线程去更新变量的值,然后写到内存中。此时对于t2来说内存值和预期值不一致了,那么就不会

                                                    再去更新这个变量的值了。

                                                          就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后从新执行刚才的操作。容易看出CAS

                                                    操作是基于共享数据不会被修改的假设。当同步冲突出现的机会很少时,这种结社能带来较大的新能提升。

 

 

 

                                深入理解java.util.concurrent.*包中各个常用类,对 自旋锁、重入锁、读写锁有深入的理解,对SYNC关键字相关的 偏向锁、轻量级锁和锁升级相关场景有深入理解。

                                                   

                                                  

 

                                深入理解 VOLATILE关键字原理以及缓存一致性协议,对使用场景有深入理解。

                                                  

                                                  Volatile通常被比喻成“轻量级的synchronized”,也是java并发编程中比较重要的关键字。和synchronized不同,volatile是一个变量修饰符,只能用来修饰变量。无法修饰方法及代码块。

                                                  Volatile的用法比较简单,只需要再申明一个可能被多线程同时访问的变量时,使用volatile修饰就可以了。

                                                  

                                                  Volatile的原理:为了提搞处理器的执行速度,在处理器和内存之间增加了多级缓存来提升。但是由于引入了多级缓存,就存在缓存数据不一致问题。

                                                  缓存一致性协议:每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行为设置成无效状态,当处理器要对这个数据进行修改操作的时候,会

                                                  强制重新从系统内存里把数据读到处理器缓存里。

                                                所以,如果一个变量被volatile所修饰的话,在每次数据变化之后,其值都会被牵制刷入主存。而其他处理器的缓存由于遵守了缓存一直性协议,也会把这个变量的值从主存加载到自己的缓存中。这就保证了一个volatile定义的变量在并发编程中,

                                                  其值的多个缓存中是可见的。

                                                  

                                                  Volatile与可见性:是值当多个线程访问同一个变量时,一个线程修改了这个变量的值 ,其他线程能够立即看的到修改的值。Java中的volatile关键字提供了一个功能,就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每

                                                  次使用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。

                                                  Volatile与有序性:有序性即程序执行的顺序按照代码的先后顺序执行,除了引入了时间片以外,由于处理器优化和指令重排等,CPU还可能对输入的代码进行乱序执行。而volatile除了可以保证数据的可见性之外,还有个欠打的功能,那就是他

                                                  可以禁止指令重排优化。Volatile可以禁止指令重排,这就保证了代码的程序会严格按照代码的先后顺序执行。这就保证了有序性。被volatile修饰的变量的操作,会严格按照代码的顺序执行,load、add、save

                                                

                                                 Volatile与原子性: 原子性是值一个操作是不可中断的,要么全部执行完成,要不就都不执行。线程是CPU调度的基本单位,CPU有时间片的概念,会根据不同的调度算法进行线程调度。当一个线程获得时间片之后开始执行,在时间片耗尽之后,

                                                 就会失去CPU使用权。所以在多线程场景下,由于时间片在线程间乱换,就会发生原子性问题。

                                                 为了保证原子性,需要通过字节码指令monitorenter和monitorexit,但是volatile和这两个指令之间是没有任何关系的,所以,volatile是不能保证原子性的。

 

                                                 以下两个场景是可以使用volatile来代替synchronized:

  1. 运算结果并不依赖变量的当前值,或者能够确保只有单一的线程会修改变量的值。
  2. 变量不需要与其他状态变量共同参与不变的约束。

 

Synchronized可以保证原子性,有序性和可见性,volatile却只能保证有序性和可见性。

 

 

                                深入理解 concurrent包中 阻塞和非阻塞队列,熟悉相关的应用场景和特性。

                                                  

 

 

                                熟悉线程池相关类,对线程池使用和拒绝策略比较熟悉。

                                                  

                                                  

 

                  

                               

        JVM

                                深入理解  引用计数法,标记清除、标记整理相关GC算法、复制算法的特性以及应用场景。

                                                        引用计数法(Java没有采用)

 

标记清除(jvm老年代回收):是最基本的收集算法,分为标记和清除两个阶段:首先标记出所有要回收的对象,在标记完成后统一回收所有被标记的对象。

特点:1、效率问题,标记和清楚的效率都不高;

         2、空间问题,标记清楚以以后会产生大量不连续的空间碎片,空间碎片太多可能会导致程序运行过程中分配较大对象的内存是,无法找到足够内存而不得不提前触发一次垃圾收集

使用地方:适合在老年代进行垃圾回收,比如CMS收集器就是采用该算法进行回收内存

 

标记整理(jvm老年代回收):分为标记和整理两个阶段:首先标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

特点:不会产生空间碎片,但是整理会华一定的时间

使用地方:合同老年代进行垃圾收集,gc收集器就是采用该算法进行回收。

                                                       

                                                        复制算法(jvm新生代回收):他先将可用的内存按容量划分为大小相同的两块,每次只是用其中的一块。当这块内存用完了,就将还存活着的对象复制到另一块上面,然后把已经使用过的内存空间一次清理掉。

                                                        特点:没有内存碎片,只要移动堆顶指针,按照顺序分配内存即可。代价是将内存缩小原来的一半。

                                                        使用地方:适合新生代区进行垃圾回收。

                                                     

                                                        按照GC的运行机制,会回收掉已经死掉的对象,而对象一半都是在年轻代就会死去,所以年轻代比老年代需要更频繁的GC清理

                                                       

                                                        复制算法与标记整理对比:复制收集算法在对象存活率较高时就要执行比较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%空间,就需要额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,

                                                        所以复制算法仅仅应用再新生代,而老年代一般不能直接选用这种算法,应该是用标记整理

 

                                                        标记整理与标记清楚对比:根据老年的特点,两种算法都是要先标记的,但是标记整理不是直接可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,可以减少内存碎片。

                           

                                                

                                深入理解jvm ClassLoader 类加载机制。

                                                  

                                                  Jvm的类加载机制主要有如下3中:

  1. 全盘负责:所有全盘负责,就是当一个类加载器负责加载某个Class时,改Class所依赖和引用其他Class也将由该类加载器负责加载,除非显示使用另外一个类加载器来载入。
  2. 双亲委派:所谓的双亲委派,则是先让父类加载器试图加载该类的.class文件,只有在父类加载器无法加载改类时才尝试从自己的类路径汇总加载该类。通俗的讲,就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,一次递归,如果父类加载器可以完成类的加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载
  3. 缓存机制:缓存机制将会保证多有加载过的class都会被缓存,当程序中需要使用某个class时,类加载器先从缓存区中搜寻该class,只有当缓存区中不存在改class对象时,系统才会读取该类对应的二进制数据,并将其转换成class对象,存入缓冲区中。这就是为什么修改class后,必须重新启动jvm,程序所做的修改才会生效的原因。

 

 

      

                                深入理解 JAVA 反射性能以及调优。

  1. java发射机制:在java运行环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用他的任意方法。这种动态获取类的信息以及动态调用对象方法的功能来自于java语言的反射机制。
          1. java中,反射是一种强大的工具。它能够创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码链接。反射允许我们再编写与执行时,使用我们的程序代码能够接入装载到JVM中的类的内部信息,而不是源代码中选中的类协作的代码。这使用反射成为构建灵活应用的主要工具。但是需要注意的是:如果使用不当,反射的成本很高。
          2. java反射机制主要提供一下功能:在运行时判断任意一个对象所属的类,在运行时构造任意一个类的对象,在运行时判断任意一个类所具有的成员成员变量和方法,在运行时调用任意一个对象的方法。
  2. java 反射机制性能优化:
          1. 通过设置setAccessible(true)的方式关闭安全检查可以达到提升反射数据的目的
          2. 用缓存,比如redi,将反射得到的元数据保存起来,适用时,只需要从内存中调用即可
          3. 利用一些高性能的反射库ReflectASM。ReflectASM使用字节码生成的方式实现了更为高效的反射机制。执行时会生成一个存取类来set/get字段,方位方法或创建实例。一看到ASM就能领悟到ReflectASM会用字节码生成的方式,而不是依赖于java本身的反射机制来实现的,所以他更快,并且避免了方位原始类型因自动装箱而产生的问题。

                    

 

                                深入理解 JVM 栈与堆的结构 ,以及线程出栈入栈相关原理,对JVM中逃逸分析、栈上替换、方法内联有深入的理解。

                                                 定义:

  1. 栈的结构:先进后出,暂存数据的地方。每个线程都包含一个栈区,栈存放在一级缓存中,存取速度较快,“栈是限定仅在表头进行插入和删除操作的线性表”  
  2. 堆的结构:先进先出,JVM只有一个堆区被所有线程所共享,堆存放在二级缓存中,调用对象的速度相对慢一些,生命周期有虚拟机的垃圾回收机制制定。
  3. 方法去:用来存放方法和static变量

                               存储的数据类型:

                                                        堆用来存储new出来的对象和数组

                                                        栈用来存储基本类型变量和对象的引用变量的地址

                                                        方法区用来存储方法和static变量

                                                       

                                                        逃逸分析是编译语言中的一种优化分析,而不是一种优化手段。通过对象的作用范围的分析,为其他优化手段提供分析数据从而进行优化。

                                                        逃逸分析包括:全局变量赋值逃逸,方法返回值逃逸,实例引用发生逃逸、线程逃逸:赋值给类变量或可以在其他线程中访问的实例变量

 

                                                        栈上分配:JVM的内存分配都是堆上进行分配的,当对象没有被引用的时候,需要依靠GC进行回收内存,如果对象数量较多的时候,会给GC带来很大的压力,也间接影响了引用的性能。为了减少临时对象内存分配的数据量,JVM通过逃

逸分析确定该对象不会被外部访问。那就通过标量替换将改对象分解在栈上分配内存,这样该对象所占用的内存空间就可以随栈帧出栈,就减轻了垃圾回收的压力。

 

                                                        标量替换:标量即不可被进一步分解的量,而Java的基本数据类型就是标量,变量队列就是可以被进一步分解的量,而这种量称之为聚合量。而在Java中对象就是可以被进一步分解的聚合量。通过逃逸分析确定该对象不会被外部访问,并且

                                                        对象可以被进一步分解时,JVM不会创建该对象,而会将该对象成员变量分解若干个被这个方式使用的成员变量所代替。这些代替的成员变量在栈帧或寄存器上分配空间。

 

                                                        同步消除:是java虚拟机提供的一种优化技术。通过逃逸分析,可以确定一个对象是否会被其他线程进行访问。

                                                                        如果在定义的类的方法上有同步锁,但是在运行时,却只有一个线程访问,此时逃逸分析后机器码,会去掉同步所运行,这就是么有出现线程逃逸的情况。那改对象的读写就不会存在资源的竞争,不存在资源的竞争,则可以消除该

                                                                        对象的同步锁。

 

                                深入理解 JIT动态编译原理,对 解释执行、编译执行有深刻理解。

                                熟悉 JVM调优、分代收集 等。

 

       框架

                               深入理解tomcat启动原理以及相关的调优,对tomcat类加载机制有深入的理解。

                               深入理解 rpc动态代理 + zk + netty 相关中间件原理和应用场景,对消息队列activmq、rabbitmq、kafka有一定的了解。

                               熟悉redis使用,对高并发下的 秒杀、分布式锁、防重等业务场景有深入理解。对缓存一致性、缓存击穿、缓存雪崩有深入理解。

                               熟悉nginux 相关的限流算法、规则、负载均衡原理以及使用场景。对流行的降级策略、降级框架有一定的了解。

                               熟练使用mybatis持久框架。

                               熟悉mycat使用、mysql的主从、读写分离有深入的理解。对mycat+mysql的分库分表有深入的理解。

                               熟悉zookeeper特性,以及在整个架构中的应用场景。对zk脑裂、故障修复有一定的了解。

                              

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值