长城软件面经

简单三分钟自我介绍

自我介绍这里一笔带过,给对面介绍自己内在 + 外在 + 校园经历 + 校园项目 + 意向岗位

技术面

1. Java基础篇

1.1 说一说数据结构

说下链表的属性,指针属性

树结构了解过吗?

叶子节点怎么存储的?

2. Spring 篇

2.1 Spring MVC 的原理

主要有5个组件,前端控制器、映射器、处理器、处理器适配器、视图解析器

  • 前端控制器也就是中央处理器,它主要负责前端用户的请求和对其他组件的转发调用;

  • 前端控制器接收到请求后,会把请求转发给处理器映射器,处理器会根据配置或注解(@Controller)找到最终要执行的Handler;

  • 然后将Handler(自己的业务处理)对象以及其对应的拦截器,封装到HandlerExecutionChain对象中并以它的形式放回;

  • 前端控制器根据获取到的Handler(Controller),去选择一个合适的HandlerAdapter(它还支持Servlet、HttpRequestHandler、ThrowawayController),成功获取到会先去执行拦截器;

  • 这时将开始对提取Request中的模型数据,并且将Handler的入参进行数据填充,以至于我们写控制层方法时,入参是前端传来的值,在填充过程中,根据自己的配置,Spring还可以帮你做一些额外操作:

    • Json/Xml 数据转换成对象
    • 格式化请求数据
    • 校验
  • Handler执行完成后,返回一个ModelAndView(视图和视图需要填充的模型数据)

  • 前端控制器根据返回的ModelAndView,选择合适的ViewResolver

  • ViewResolver 结合Model和View,来渲染视图

  • 最终前端控制器将渲染结果返回给客户端

3. JVM 篇

3.1 说一说你对jvm内存结构的理解

jvm 内存模型由程序计数器、方法区、虚拟机栈、本地方法栈、堆和运行常量池组成。

方法区:主要是存储类变量,静态变量,常量以及class描述信息(会在类加载后加载到方法区中)

虚拟机栈:虚拟机栈跟线程是直接挂钩的,它的生命周期跟线程一样,而且虚拟机栈设置的大小(-Xss1024k,1024个字节1M单词缩写Stack Size),当设置过大时,在堆大小固定的情况下,总内存 = 堆内存 + 线程数 * 虚拟机栈大小,可线程数就会相应减少;

本地方法栈:跟虚拟机栈相似,不过不是用Java写的;

程序计数器:线程私有,每个线程在让出CPU会由它来记录当前位置,以便恢复CPU还在原位置执行;

堆:存放Java对象实例,在JDK7版本中,由新生代(Eden区、Survivor区)和 老年代组成,也是GC垃圾收集器处理的主要区域,俗称GC堆,是管理内存中最大的一块;

运行时常量池是放在方法区中的,作为方法区的一部分。

3.2 对象创建和销毁的过程

对象创建的过程其实在 jvm 中包括了加载、链接(包含验证(虚拟机的安全性、对象头、父类与子类中final等声明的安全性校验等等)、准备、解析)、初始化;

准备阶段:申请内存空间并完成默认的初始化,比如int类型默认初始化为4个字节的内存空间,并初始化为0;

解析阶段:将常量池中的引用解析为直接引用,也就是将类中的常量池放到了运行时常量池中,这样我们在直接调用方法或者引用类的时候,是需要符号引用的,只不过jvm帮我们自动引用了常量中的符号,就成直接引用。

初始化阶段:用户真正的赋值操作,即调用相应的构造器构造对象,构造对象创建的时候,如果是比较复杂的,由于虚拟机优化机制,会出现指令重排序问题,什么问题?

在该场景中,构造过程分为几个步骤:申请内存 -> 构造块构造 -> 内存地址返回,这里面的指令为了性能上的提示,而不改变结果,jvm可能会将内存地址也就是构造函数创建的对象指向的内存地址放到构造块过程中返回,而没有等到最后。

引用:编译器可以自由地以优化的名义对指令的顺序进行随意处理。

那么怎么解决?

当然是保证原子性呀!

怎么保证?

final声明不仅能保证类不可继承,方法不可重写,变量不可变,还有一个重要的特点就是能保证可见性,媲美volatile

可见性保证就是对象如果已经可见(地址已经返回了,那么保证它构造的完整性,也就是指令不会发生重排)性

文章分享:https://qa.1r1g.com/sf/ask/451997661/

内存模型官方讲解:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html

推荐比较好的网站:https://qa.1r1g.com/

补充

如果类中的成员变量声明为final并且是基本类型,那么赋值阶段会在准备阶段就完成了,这些可实现一些并发安全性应用;

3.2 GC 垃圾回收了解过吗

了解过一点点,没了,他就不问了……

我就是专门去看了gc中的一些原理,比如复制算法、并发标记、并发清除、分代回收,还有SerialparNewparallel Scavengeg1这些,看來不能太低调了。

3.3 JVM 调优了解过吗

答案是没有,所以没有下文了,可惜这篇面经比较卑微,味道不够香。

因为就是简单使用过,没有在具体项目场景用到,不敢吹嘘。

3.4 volatile 能保证原子性吗

不能,volatile可以保证可见性,它是在jvm优化后,出现指令重排序后才有的,能够防止指令的重排序,也就是说指令重排序可能会先将对象在执行构造函数中,提前把对象所在地址给返回了,这就可能导致成员变量出现为null的情况,有了volatile就不会发生。

volatile一般修饰于变量

当然除了这个,volatile保证可见性还体现在内存与高速缓存之间,如果一个变量通过for循环去执行,那么jvm将进行性能优化,将内存中的变量放到高速缓存中,这样容易出现内存与高速缓存的不一致性问题,volatile可以保证对内存中变量的修改及时同步到高速缓存中。

4. SQL 篇

4.1 聊一聊你对SQL中的优化

主要分为语句优化和索引优化

语句优化比如避免使用or,可能导致全盘遍历,我是说了join连接和子查询,前一个只需要查询一次,后一个会多次遍历全表,如果数据量大的表将很严重。

索引优化的话,面试官很耐心给我讲了组合索引、失效的问题

4.2 SQL 中的函数的处理

SQL中的函数比如时间的yyyy-MM-dd hh-mm-ss,通过这种设置可以来处理相应的时间

可以看看这篇文章:https://blog.csdn.net/chentaocba/article/details/7539409

可以这样自定义去处理拼接时间

5. Redis 篇

5.1 聊一聊哨兵模式

哨兵模式的应用其实是Redis集群中故障恢复和高可用的应用,在Redis读写分离也就是主从复制中,哨兵节点作为独立节点,不参与选举。

在主节点也就是master节点宕机后,多个哨兵节点一致发现主节点下线,通过投票选举哪一个从节点选举为主节点。

其实就是一种监控主节点是否故障的行为。

5. 分布式

3.1 说一说服务降级

在请求超时、资源不足、CPU、数据库IO达到瓶颈时,采取的一种补救措施。

服务降级主要有DubbboMockSpring CloudHystrix,屏蔽对远程的调用,一般使用SpringBoot比较推荐整合Hystrix来实现服务降级容错。

3.2 聊一聊你的项目分布式中怎么解决出现的问题

我做的是一个分布式的博客管理项目,在搭建并部署分布式项目时,调用远程服务的时候,注册中心Nacos很容易出现宕机,当时排查的原因是内存不够,当时使用了阿里的两台服务器、一台腾讯云服务器来搭建分布式项目,阿里一台作为前端和Nginx做反向代理,一台运行着博客的文章、评论、分类标签和用户的五个服务,一台作为代理服务器,处理前端的请求、远程调用腾讯云服务器的服务,但我把注册中心放到了腾讯云上,因为腾讯云上的服务需要进行服务注册,而且注册中心Nacos启动是带有Web后台管理页面的,所以就作为一个内部的服务器使用。

于是解决方案就是将多个服务进行横向拆分,将文章和用户的服务放到代理服务器上运行,解决了在远程调用服务的时候因为请求超时或资源不足导致Nacos注册中心出现宕机的情况。

6. 网络编程

6.1 Tomcat 与 Netty 有什么区别

Tomcat是一种使用http协议的web容器,是用来启动web应用的,而Netty是一种自定义处理各种字节流数据做相应的编解码,也就是可以自定义通信协议,Netty是一款基于NIO的网络通信框架,而且Tomcat的话其实也支持NIO模式。