天天看点

腾讯Java研发实习一面面经20190322

惯例的自我介绍。其实自我介绍也挺有学问的,自我介绍的方面很大程度决定了接下来的问题。

我先简单的介绍了一下自己,然后说了一下最近在学习的东西,然后说对Java很感兴趣,喜欢做很酷的事情。最后说了一下自己的项目。

1.说说Java的内存模型吧

Java的内存模型主要分为两大块,线程共享区和线程独享区。

独享区就是我们平时说的栈,主要包括虚拟机栈,本地方法栈和程序计数器,两个栈都是用来调用方法的,栈中的栈帧是对一个方法模型的描述。程序计数器则是对当前代码执行位置的一个标注,用于方法返回等。

共享区就是我们平时说的堆,堆中有通过new关键字生成的对象和一些运行时常量。

2.Java堆是怎么划分的

感觉他想让我说说方法区和堆的区别,因为我上个问题没说这些。

在早期的JDK版本中,整个内存共享区分为堆和方法区,堆中存放new关键创建的对象,细分可以分为新生代和老年代,这时根据垃圾收集器策略分的。然后方法区中的运行时常量池使我们存放常量和基本类型变量用的。

在JDK8之后的版本中,方法区的一部分融入了堆中,即常量和基本量也存放到了堆中,并开辟了一块新的内存区域“元空间”。

3.你对JVM进行过调优吗?或者你有没有调整过Java的大小

在项目中并没有,因为对虚拟机的了解程度我还做的不够好,擅自调试可能会造成无法预料的结果。在学习JVM时调整堆大小的参数我都自己操作过,使用了Xms(此处省略,大概就是几个虚拟机参数)。

4.那么Java堆是越大越好吗?

并不是,Java堆的增大确实可以存放更多的对象,但是在回收对象和维护的时候会占用更多的资源。在新发布的JDK12中新加入了停顿时间更短的新垃圾收集器,这个我不太清楚的它的底层原理,除了这个,就算是最新的G1收集器,也在收集垃圾对象时要造成程序运行的停顿,如果我们只是一味的增大堆内存而不调优,负面影响也会增大。

5.你了解ThreadLocal吗,说一下

在我的理解中,ThreadLocal其实也是一种键值存储容器,只不过它的键固定为线程对象,值可以指定。我在没使用Spring框架时处理事务时用过它,通过ThreadLocal绑定开启事务的连接对象,然后在业务平行调用的时候通过在ThreadLocal中获取连接对象提交事务。

6.ThreadLocal中的值一定是线程安全的吗?

(这个问题我一开始没有太理解,面试官很好,给我解释了一下。大概就是问 A线程往ThreadLocal中放了一个对象,B线程能不能访问到 这个问题。)

我:应该是安全的,B线程和A线程是两个线程,在ThreadLocal相对于两个键,B线程无法获取A线程的值,但是ThreadLocal中存放的也是在对象的引用,如果这个对象没有被其他对象引用的,是无法改变的。

(其实ThreadLocal本来就不是拿来解决线程安全问题的,如果存放到ThreadLocal中对象如果是共享变量,其他线程还能通过引用获取到这个变量就不是线程安全的了,因此不管说安全还是不安全,一定说出这个对象能否通过其他引用获取到。)

7.我看你在项目中使用session和token双重认证,说一下吧

Session是服务器在内存中开辟一段空间存放用户此会话信息,并返回给用户SessionID,下次请求通过请求头中的SessionID就可以拿到该用户Session域中的信息。我用Session做了用户的自动登录,每次通过SessionID判断用户是否已在登录状态。

Token是用来判断该次请求的安全性的,在提交信息的网页的表单处加上一个hidden的随机字符串作为Token,每次都会更换。服务器每次都校验该Token,只有是最新的token才放行。这样可以解决表单重复提交问题,而且防范CSRF攻击。

8.CSRF攻击是什么,怎么到达防范的

CSRF是跨域请求伪造攻击。大体上就是当用户持有A网站的通行证时,黑客通过获取用户的cookie获得通行证,然后拿着这个通行证去访问A网站。但由于token的存在,黑客无法拿到含有最新的token的页面,因此在冒充用户访问A网站时无法通过验证(给面试官讲的时候忘记了很多专有名词,导致这一段讲的不太清楚,也不知道面试官听明白了没有。。。。)

CSRF攻击与防范

9.如果应用部署在多台服务器上,使用session会发生什么问题,怎么解决

因为Session一般只会存储到当前访问的服务器上,如果同一用户的下次请求被分配到了其他的服务器上时就无法访问之前的Session域了。解决方法是使用Redis缓存集群,通过SessionID存储用户信息到缓存集群中,这样多个服务器都可以通过缓存得到用户SessionID对应的信息了。

10.你项目在通过双重检查锁防止缓存穿透,怎么做的

我们一般在同一需求的请求第一次到达时从数据库检索信息,然后放到缓存中。之后该需求的请求到达时直接从缓存中取值。在高并发时,第一个请求正在数据库检索信息未结束是其他请求就会判断缓存中没有响应值也进入数据库查询,导致多个线程查询数据库。双重检查锁通过 检查-加锁-检查 的模式避免了多次查询数据库的问题。(这里面试官好像没听太明白,我又解释好长时间。有时候几行代码的事,解释好长时间也说不清,当然我的表达能力也有问题)

11.我看你数据库中用了排序,那你用索引了吗?

用了

12.如果你有一个表,只有性别和姓名两列,给性别加个索引可以吗?

可以,但我认为这样做不太好,因为性别只有男,女两个值,就算我们使用了索引,得到的检索结果是所有的男人或者所有的女人,还是一个很庞大的数据集。这样并不能达到我们使用索引的初衷,而且插入信息时还得维护索引,这样想的话这个索引就是弊大于利的。

13.你使用innoDB引擎是吧,那么他的索引是怎么存放的呢?

innoDB整个数据表就是通过一个大的聚簇索引存储的,该索引是通过主键建立的。聚簇索引是一个B+树结构,它的叶子节点上直接存放着每条数据的信息。初次之外我们建的索引都是二级索引,二级索引的叶子节点存放的是主键信息。一般我们通过索引检索信息需要走两个检索B+树的过程,先通过二级索引找到相应的主键,然后通过聚簇索引找到相应值。

14.事务中的RollBack是怎么实现的?

我使用的是innoDB引擎,比起myisam引擎,它锁的粒度更小。myisam在事务中锁表,innoDB锁行,并且在每行数据都加入两个列记录该行的最后修改时间和删除时间。在事务中,获取类似于快照的操作行的数据,然后进行一系列操作。如果事务正常结束就写回表,如果发生异常就不回写。(这里我感觉自己解释的不太好,在《高性能Mysql》这本书上有详细说明)

15.你把tomcat,mysql,redis都部署在一台服务器上,如果redis发生了内存泄漏,就会挤压tomcat和mysql的内存。为了避免这种情况,你怎么给他们规划内存,或者你用过解决类似问题的工具吗?

这个是操作系统的问题,我不太擅长,学校操作系统教的很浅,自己也没有系统的学。我记得之前看过一本书说linux和windows每个进程最多只分配3g的内存来着,按这个理来说不可能发生上述情况,就这样告诉面试官了2333333。然后他笑着说的确可能发生。然后我说不太了解这个。。。。

16.进程间通讯的方法

管道通讯和通过共享变量通讯。。。。在想不到了。

(操作系统我好弱啊)

进程间的通讯方式

17.还有什么想问我的吗?

对我这次的面试点评一下吧。

挺好的,你的Java基础很扎实,但是平时在学习时除了编程语言方面还要多注重计算机基础的学习。

小结

可能是腾讯是以C系语言用的多吧,感觉面试官并没有问过多关于Java源码方面的知识,反倒是JVM底层原理涉及的多。并且问的特别全面,这个学科的生态都会涉及。在这次面试中,面试官问我有没有接触过分布式啥的,我直接说没有2333。问我计算机网络学过吗,我说现在大三下,学校刚开这门课,他算了一下好像确实是这样,然后就跳过了。这个面试官真的特别好,我面试途中一度紧张的呼吸不协调,他还笑着和我说不要紧张,遇到这样的面试官真的很幸运。