2019有赞中高级Java工程师面试题与解答
程序员文章站
2022-05-18 17:14:08
1. 说说JVM的内存分区 线程私有的区域 程序计数器:JVM中程序计数器相当于汇编语言中的CPU中的寄存器,保存程序当前执行的指令的地址。 虚拟机栈:Java方法执行的栈由许多个栈帧构成,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operan ......
-
说说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栈和堆什么时候会发生内存溢出
- 栈溢出:递归调用超过栈帧的深度,就会出现*error。
- 堆溢出:堆内存不足,且gc来不及或者无法回收内存时,就会发生堆溢出,比如在集合中大量创建对象直至堆内存耗尽。
-
软引用和弱引用、虚应用的区别和使用场景。
- 软引用:描述一些有用但非必需的对象,用java.lang.ref.softreference类来表示。对于软引用关联着的对象,只有在内存不足的时候jvm才会回收该对象,适合用来实现缓存。
- 弱引用:弱引用的对象拥有更短暂的生命周期,用java.lang.ref.weakreference类表示。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
- 虚引用:顾名思义就是形同虚设,用java.lang.ref.phantomreference类表示。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
-
java的锁有哪些,怎么使用、实现原理是什么
- synchronized:用于方法或者对象的同步锁,实现原理是操作系统的互斥锁,属于重量级锁。在jdk6以后加入锁升级,提高了性能。
- lock:基于aqs的可重入的轻量级锁,实现类有reentrantlock、readwritelock、reentrantreadwritelock。相比synchronized,它的使用更加灵活,且需要手动释放锁。
说说synchronized锁升级的过程
参考:tomcat类加载器结构
参考: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无法同时做到。
参考: 哪些技术是牺牲了一致性来保证可用性
mysql主从复制就是典型的牺牲c,保证a的做法。如何高效处理10亿个数字去重
采用bitmap,参考:如何在十亿个数找前10个最大的数
采用最小堆算法,参考: