阿里巴巴三面

阿里巴巴蚂蚁金服面试总结

  1. 讲一下Java中的内存模型。

答:Java中的内存模型是一个抽象的概念,分为主内存和工作内存,所有的变量都存在主内存中(局部变量和方法参数除外),每个线程都有一个自己的工作内存,当访问到主内存中的某些变量的时候,会将该变量拷贝一份到自己的工作内存中。

当数据从主内存复值到工作内存时,必须出现两个动作:第一,由主内存执行的读(read)操作;第二,由工作内存执行的相应的load操作;当数据从工作内存拷贝到主内存时,也出现两个操作:第一个,由工作内存执行的存储(store)操作;第二,由主内存执行的相应的写(write)操作。

每一个操作都是原子的,即执行期间不会被中断。

对于普通变量,一个线程中更新的值,不能马上反应在其他变量中。

如果需要在其他线程中立即可见,需要使用volatile关键字。

Java的内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的。

原子性:原子性是指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干扰。i++不是原子操作。

可见性:可见性是指当一个线程修改了某一个共享变量的值,其他线程时否能够立即知道这个修改。

有序性:在并发时,程序的执行可能就会出现乱序。在本线程内,操作都是有序的;在线程外观察,操作都是无序的。(指令重排或主内存同步延时)

 

Happend-Before规则

·程序顺序原则:一个线程内保证语义的串行性

·volatile规则:votatile变量的写,先发生于读,这就保证了volatile变量的可见性

·锁规则:解锁(unlock)必然发生在随后的加锁(lock)前

·传递性:A先于B,B先于C,那么A必然先于C

·线程的start()方法先于它的每一个动作

·线程的所有操作先于线程的终结(Thread.join())

·线程的中断(interrupt())先于被中断线程的代码

·对象的构造函数执行结束先于finalize()方法

 

  1. JVM启动流程

答:

在启动过程中,JVM会先根据当前路径和系统版本去查找jvm.cfg配置文件,然后根据配置文件找到对应的jvm.dll文件,初始化JVM并加载JNIEnv接口,最终找到main()函数,开始执行。

  1. Java中,GC的对象是堆空间和永久区。
  2. 什么是引用计数法?

答:引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1。只要对象A的引用计数器的值为0,则对象A就不可能再被使用。

引用计数法的问题:

·引用和去引用伴随加法和减法,影响性能

·很难处理循环引用。对象A中引用了对象B,对象B中引用了对象A。

 

引用计数法是用来判断对象是否可以被回收的算法,但java中并没有用到,Java中用来判断对象是否可以被回收的算法是可达性分析算法

  1. 什么是可达性分析算法?

答:从根节点开始向下进行搜索,如果一个对象无法通过根节点搜索到,说明该对象是不可达的,即不可用的,要被回收;如果一个对象可以通过根节点搜索到,说明该对象是可达的,即可用的,不用被回收。

 

  1. 什么是标记-清除算法?

答:标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。。

  1. 什么是标记-压缩算法?

答:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化、和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间

  1. 标记-压缩对标记-清除而言,有什么优势?

答:

  1. 什么是复制回收算法?

答:与标记-清除算法相比,复制算法是一种相对高效的回收算法。不适用于存活队形较多的场合,如老年代。将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

  1. 什么是分代回收算法?

答:这种算法并没有什么新思想,只是依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。然后根据不同代的特点,选取合适的收集算法。新生代每次垃圾回收只有少量对象存活,采用复制回收算法。老年代中的对象存活率高,采用标记-清除或标记-整理算法。

  1. 内存溢出一般发生在哪个区?永久代会不会导致内存溢出?

答:

  1. JVM Heap(堆)溢出:java.lang.OutOfMemoryError: Java heap space
    JVM在启动的时候会自动设置JVM Heap的值, 可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap的大小是Young Generation 和Tenured Generaion 之和。在JVM中如果98%的时间是用于GC,且可用的Heap size 不足2%的时候将抛出此异常信息。
    解决方法:手动设置JVM Heap(堆)的大小。

自我总结:堆内存大小可通过参数进行设置,默认为20M,新生代10M,老年代10M,当堆内存中98%的时间都用于GC,且可用的堆内存空间不足2%时将抛出内存溢出异常。
2)PermGen space溢出: java.lang.OutOfMemoryError: PermGen space
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被JVM存放Class和Meta信息的,Class在被Load的时候被放入PermGen space区域,它和存放Instance的Heap区域不同,sun的 GC不会在主程序运行期对PermGen space进行清理,所以如果你的APP会载入很多CLASS的话,就很可能出现PermGen space溢出。一般发生在程序的启动阶段。
解决方法: 通过-XX:PermSize和-XX:MaxPermSize设置永久代大小即可。

自我总结:方法区中的永久区也会发生内存溢出异常,这块区域主要是JVM加载的Class文件存放的区域,垃圾回收器并不会在主程序运行期对永久区进行垃圾回收,所以如果一次性加载的Class文件过多的话,容易出现永久区内存溢出问题。一般发生在程序的启动解阶段。
3)栈溢出: java.lang.StackOverflowError : Thread Stack space
栈溢出了,JVM依然是采用栈式的虚拟机,这个和C和Pascal都是一样的。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。 通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K的空间(这个大约相当于在一个C函数内声明了256个int类型的变量),那么栈区也不过是需要1MB的空间。通常栈的大小是1-2MB的。通俗一点讲就是单线程的程序需要的内存太大了。 通常递归也不要递归的层次过多,很容易溢出。
解决方法:1:修改程序。2:通过 -Xss: 来设置每个线程的Stack大小即可。

自我总结:虚拟机栈中也会发生内存溢出问题,假如你调用一个无限递归的递归函数,虚拟机栈就会发生内存溢出问题。

  1. 动态加载类的框架了解哪些?

答:Spring。SpringMVC,Mybatis等。

  1. 动态代理一般有哪几种是实现方式?动态代理的应用场景有哪些?

答:两种。JDK动态代理和cglib动态代理。

动态代理的应用场景:

Spring AOP

Mybatis框架中的Mapper接口的实现类是由mybatis通过动态代理的方式自动生成的,我们程序员只需要定义Mapper接口,而不需要写实现类。

JDK动态代理和cglib动态代理的区别?

答:JDK动态代理只能对实现了接口的类生成代理对象;

cglib可以对任意类生成代理对象,它的原理是对目标对象进行继承代理,如果目标对象被final修饰,那么该类无法被cglib代理。

  1. 栈会不会溢出?栈溢出一般抛什么异常?

答:栈会溢出。栈溢出会抛java.lang.StackOverflowError

  1. Java类加载机制?

答:Java的类加载机制分为加载、验证、准备、解析和初始化这5个阶段,其中,验证、准备、解析3个阶段统称为连接。

加载阶段。虚拟机利用类加载器将Class文件加载到内存中,准确的讲,是加载到内存中的方法区,并为这个Class文件生成一个Class文件对象(类的字节码对象),作为方法区中这个Class文件的访问入口。

验证阶段。这一阶段的目的是为了确保Class文件中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备阶段。这一阶段是正式在方法区内为类变量分配内存,并设置初始值。

解析阶段。这一阶段是将类文件中的符号引用替换为直接引用。

初始化阶段。执行类中定义的java代码,初始化类变量和其它资源

  1. HashMap、Hashtable、ConcurrentHashMap的区别?

答:Hashtable是线程安全的,效率低,不允许null键null值,Hashtable的hash数组默认大小是11,增加的方式是old*2+1。

HashMap是Hashtable的轻量级实现,是线程不安全的,效率高,允许null键null值,HashMap的hash数组默认大小是16,增加的方式是old*2。

ConcurrentHashMap采用锁分段技术有效提升了并发访问率。它将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据的时候,其他段的数据也能将其他线程访问。

  1. 简单介绍一下java的反射机制?反射在哪些地方有应用场景?

答:java中,我们要想知道一个类里面有哪些属性和方法,要想创建一个对象并调用对象里面的属性和方法,我们首先要拿到这个类的.java文件,但是有些情况下,我们无法获取到这个类的.java文件,只能获取到这个类编译后的.class文件,如何通过这个.class文件,去知道这个类里面有哪些属性和方法,如何通过这个.class文件去创建这个类的实例对象,并调用里面的属性和方法,这种技术就叫java的反射机制。

反射的应用场景:

JDBC连接数据库,第一步加载数据库驱动Class.forName(“com.mysql.jdbc.Driver”),用的就是反射技术。

Spring加载Bean。

  1. Spring加载Bean的流程?

答:假如我用Bean标签配置了一个Person对象,在spring容器中起名为person。

ApplicationContext ac = new ClassPathXmlApplicationContext(“spring-context.xml”);

Person person = ac.getBean(“person”);//这一步用到了反射,person这个id对应的是Person对象的类路径。通过一个类的class文件获取到这个类的实例对象,典型的反射。

ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了,那么必然第一行就已经完成了对所有Bean实例的加载。通过观看源码,总结如下:

Spring中bean的加载过程(太抽象,不懂就不懂吧!)

1.获取配置文件资源

2.对获取的xml资源进行一定的处理检验

3.处理包装资源

4.解析处理包装过后的资源

5.加载提取bean并注册(添加到beanDefinitionMap中)

19.讲一下Java中的线程池,各个参数的作用,如何执行任务的?

答:java中的线程池主要通过ThreadPoolExecutor类来实现,在创建ThreadPoolExecutor类的实例化对象时,有这么几个参数需要配置:

核心线程池大小(corePoolSize)、最大线程池大小(maximumPoolSize)、workQueue(任务队列)、保持存活时间(keepAliveTime)、unit(保持存活时间单位,秒或分钟等)。

当一个任务被添加进线程池时,需要经过一下步骤:

(1)线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务。

(2)线程数量达到了corePoolSize,则将任务添加到任务队列进行缓存。

(3)当任务队列已满,新建线程(非核心线程)执行任务。

(4)当任务队列已满,并且总线程数又达到了maximumPoolSize,就会抛出异常。

20.mysql的存储引擎有哪几种?

答:myisam、innodb、memory等。

21.myisam和innodb的区别?

答:myisam保存文件的方式是一张表对应三个文件(结构文件、数据文件、索引文件),数据和索引分离;不支持事务,不支持外键,锁表。

innodb:保存文件的方式是一张表对应两个文件(结构文件、数据索引文件),索引和数据绑定在一起;支持事务,支持外键,锁行。

22.myisam和innodb存储引擎都支持B+Tree索引,这两者有什么区别吗?

答:使用myisam引擎,一张表对应三张保存文件(结构文件、数据文件、索引文件),也就是说使用myisam引擎,数据文件和索引文件是分开的,我们称之为非聚集索引。

使用innodb引擎,一张表对应两张保存文件(结构文件、数据索引文件),也就是说使用innodb引擎,数据文件和索引文件是绑定在一起的,我们称之为聚集索引。

  1. 数据库优化有哪些着手点?

答:(1)表的设计合理化(符合3NF)

(2)添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]

(3)分表技术(水平分割、垂直分割)

(4)主从复制,读写[写: update/delete/add]分离

(5)存储过程 [模块化编程,可以提高速度]

(6)对mysql配置优化 [配置最大并发数my.ini, 调整缓存大小 ]

(7)mysql服务器硬件升级

(8)定时的去清除不需要的数据,定时进行碎片整理(MyISAM) 

24.springMVC处理请求的流程?

答:

  1. 事务的四大特性?

答:ACID。

  1. 什么是脏读?幻读?不可重复读?

:脏读、幻读、不可重复读是事务中的并发问题。

脏读:读到了某一个正在操作但还没有提交的数据,这样读到的数据很可能不是最终确定的数据,很可能那个人还回滚了。

幻读:往往是针对的整表的操作,比如经理让你将整张user表都删掉,结果在你删的期间,有人往这张表中加了一条记录,当你删完之后,你觉着数据库中没有这张表了,但是一查,这张表中还有一条数据,这个时候你觉着自己产生了幻觉,怎么还有一条呢?明明都已经删了。这就叫幻读。

不可重复读:两次连续的读取,数据不一致,可能是有人在你第一次读完之后将数据修改并提交了,导致你再读取的时候,数据和第一次读取的不一致,不知道该用哪一次。

  1. 事务中有几种隔离级别?

答:读未提交、读已提交、可重复读、串行化。

  1. 事务中的传播行为指什么?有几种?

答:事务的传播行为指的是在业务方法之间平行调用时,如何来处理事务的问题。

Spring中定义事务传播行为的属性值有7种,分别是:required、requires_new、supports、not_supported、mandatory、nested、never。我们常用的是propagation=”REQUIRED”,默认的就是REQUIRED,指的是支持当前事务,如果不存在,就新建一个。

  1. 描述一下TCP协议的三次握手

答:发送端发送一个带SYN标志的数据包给接收端

  接收端返回一个带SYN和ACK标志的数据包给发送端,表示确认信息

  发送端再发送一个带ACK标志的数据包给接收端,表示“握手”结束

  1. 描述一下TCP协议的四次挥手

答:1.客户端向服务端发送一个FIN包,用来关闭客户端到服务端之间的数据传送

       2.服务端收到FIN包后,向客户端发送一个带有ACK标志的数据包,表示确认信息

       3.服务端向客户端发送一个FIN包,用来关闭服务端到客户端之间的数据传送

       4.客户端收到FIN包后,向服务端发送一个带有ACK标志的数据包,表示确认信息

“四次挥手”详解:

        假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!

  1. get和post请求的区别?

答:

  1. cookie和session的请求?

答:

  1. 了解哪些开源的中间件?缓存?消息?分布式框架?

答:

  1. 用到过哪些设计模式?单例模式的实现?

答:

 

猜你喜欢

转载自blog.csdn.net/lz1170063911/article/details/81364921