2019有赞中高级Java工程师面试题与解答
-
说说JVM的内存分区
- 线程私有的区域
- 程序计数器:JVM中程序计数器相当于汇编语言中的CPU中的寄存器,保存程序当前执行的指令的地址。
- 虚拟机栈:Java方法执行的栈由许多个栈帧构成,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。
- 本地方法栈:与虚拟机栈相似,本地方法栈是为执行本地方法服务的。在JVM规范中,并没有强制规定本地方法栈的具体实现方法以及数据结构。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。
- 线程共享的区域
- 方法区:存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。
- 堆内存:用来存储对象本身的以及数组(数组引用是存放在Java栈中的)。只不过和C语言中的不同,在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。因此这部分空间也是Java垃圾收集器管理的主要区域。在JVM中只有一个堆。
- 线程私有的区域
-
Java对象的回收算法
- 分代收集法:它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记整理或者标记清除。
- 标记清除法:将需要回收的对象标记出来,批量清除。缺点是效率低,产生碎片。
- 标记整理法:将需要回收的对象标记出来,移动到一端再清除,避免了碎片的产生。
- 复制法:将新生代内存划分为8:1:1三部分,较大的叫Eden区,其余是两块较小的Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。
-
CMS和G1了解么,CMS解决什么问题,说一下回收的过程。
CMS,全称Concurrent Mark and Sweep,用于对年老代进行回收,目标是尽量减少应用的暂停时间,减少full gc发生的机率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停。 -
CMS回收为什么要停顿两次
当虚拟机完成两次标记后,便确认了可以回收的对象。但是,垃圾回收并不会阻塞我们程序的线程,他是与当前程序并发执行的。所以问题就出在这里,当GC线程标记好了一个对象的时候,此时我们程序的线程又将该对象重新加入了“关系网”中,当执行二次标记的时候,该对象也没有重写finalize()方法,因此回收的时候就会回收这个不该回收的对象。虚拟机的解决方法就是在一些特定指令位置设置一些“安全点”,当程序运行到这些“安全点”的时候就会暂停所有当前运行的线程(Stop The World 所以叫STW),暂停后再找到“GC Roots”进行关系的组建,进而执行标记和清除。 -
Java栈和堆什么时候会发生内存溢出
- 栈溢出:递归调用超过栈帧的深度,就会出现StackOverflowError。
- 堆溢出:堆内存不足,且GC来不及或者无法回收内存时,就会发生堆溢出,比如在集合中大量创建对象直至堆内存耗尽。
-
软引用和弱引用、虚应用的区别和使用场景。
- 软引用:描述一些有用但非必需的对象,用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象,适合用来实现缓存。
- 弱引用:弱引用的对象拥有更短暂的生命周期,用java.lang.ref.WeakReference类表示。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
- 虚引用:顾名思义就是形同虚设,用java.lang.ref.PhantomReference类表示。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
-
Java的锁有哪些,怎么使用、实现原理是什么
- synchronized:用于方法或者对象的同步锁,实现原理是操作系统的互斥锁,属于重量级锁。在JDK6以后加入锁升级,提高了性能。
- Lock:基于AQS的可重入的轻量级锁,实现类有ReentrantLock、ReadWriteLock、ReentrantReadWriteLock。相比synchronized,它的使用更加灵活,且需要手动释放锁。
-
说说synchronized锁升级的过程
参考:https://www.cnblogs.com/xiaoyangjia/p/11457994.html -
Tomcat类加载器结构
参考:https://www.jianshu.com/p/c205a74aa4da -
Spring中如何让A和B两个bean按顺序加载
采用dependOn注解 -
beanfactory和applicationcontext是什么关系,有什么区别
- BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
- 继承MessageSource,因此支持国际化。
- 统一的资源文件访问方式。
- 提供在监听器中注册bean的事件。
- 同时加载多个配置文件。
- 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
- BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
- ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
- 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
- BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
- BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
- BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
-
MySQL里如何做一条SQL的优化
采用explain查看SQL的执行计划,比如执行explain select id from orders
,
出来的信息有10列,分别是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra,比较重要的列:- key:表示实际使用的索引
- ref:列与索引的比较
- rows:扫描出的行数
通过查询计划的信息,判断这条SQL是否需要增加索引、修改写法等等。
-
MySQL集群的主从复制怎么做的,具体有哪些线程做哪些事情,使用了哪些日志。
- 主库在事务提交时,会把数据库变更作为事件记录在二进制文件binlog中;mysql主库上的sys_binlog控制binlog日志刷新到磁盘。
- 主库推送二进制文件binlog中的事件到从库的中继日志relay log,之后从库根据中继日志重做数据库变更操作。通过逻辑复制,以此来达到数据一致。
- Mysql通过3个线程来完成主从库之间的数据复制:其中BinLog Dump线程跑在主库上,I/O线程和SQl线程跑在从库上。当从库启动复制(start slave)时,首先创建I/O线程连接主库,主库随后创建Binlog Dump线程读取数据库事件并发给I/O线程,I/O线程获取到数据库事件更新到从库的中继日志Realy log中去,之后从库上的SQl线程读取中继日志relay log中更新的数据库事件并应用。
-
CAP定理了解吗,为什么只能三选二,为什么分区容忍性必须保证
- Consistency(一致性):多个节点之间的数据保持一致。
- Availability(可用性):只要收到用户的请求,服务器就必须给出回应。用户可以选择向 G1 或 G2 发起读操作。不管是哪台服务器,只要收到请求,就必须告诉用户,到底是 v0 还是 v1,否则就不满足可用性
- Partition tolerance(分区容错性):多个节点之间必然出现无法通信,分布式系统必须接收这种状况。
一般来说,节点故障、网络故障是常态,分区容错无法避免,因此CAP的P总是成立,剩下的C和A无法同时做到。
参考:http://www.ruanyifeng.com/blog/2018/07/cap.html -
哪些技术是牺牲了一致性来保证可用性
MySQL主从复制就是典型的牺牲C,保证A的做法。 -
如何高效处理10亿个数字去重
采用bitmap,参考:https://www.jianshu.com/p/b39eb55d4670 -
如何在十亿个数找前10个最大的数
采用最小堆算法,参考:http://doc.okbase.net/zyq522376829/archive/169290.html
面试题来源:https://www.nowcoder.com/discuss/97444
公 众 号:编码专家
独立博客:codingbrick.com
文章出处:https://www.cnblogs.com/xiaoyangjia/p/11555197.html
本文版权归作者所有,任何人或团体、机构全部转载或者部分转载、摘录,请在文章明显位置注明作者和原文链接。