【Java校招面试】实战面经(二)

本文涵盖了Java面试中常见的技术点,包括synchronized与Lock的区别、GC触发机制、JVM调优、海量数据存储设计、缓存原理与注意事项、JVM内存区域、操作系统页式存储、volatile内存可见性、happens-before原则、Java锁类型、Http请求原理、TCP连接特点、AOP原理、代理实现及设计模式的应用等。内容深入且全面,适合准备Java面试者参考学习。
摘要由CSDN通过智能技术生成


前言

“实战面经”是本专栏的第二个部分,本篇博文是第二篇博文,如有需要,可:

  1. 点击这里,返回本专栏的索引文章
  2. 点击这里,返回上一篇《【Java校招面试】实战面经(一)》
  3. 点击这里,前往下一篇《【Java校招面试】实战面经(三)》

一、synchronized与Lock的区别,使用场景

1. 区别:
  1) synchronized是关键字,Lock是一个类;
  2) Lock需要手动加解锁,而synchronized不需要;
  3) Lock可以通过锁对象获取锁的状态信息,而synchronized不能;
  4) Lock可以实现公平锁,而synchronized只能是非公平的;

2. 使用场景:
并发不严重时使用synchronized,高并发时使用Lock。因为synchronized是阻塞式的,前一个线程没有释放锁之前,后一个线程必须阻塞等待,而Lock可以设置超时。


二、Minor GC和Full GC的触发机制

1. Minor GC的触发机制: 新生代的Eden区空间不足

2. Full GC的触发机制:
  1) 程序中手动调用了System.gc();
  2) 老年代空间不足;
  3) 从新生代转入到老年代的对象的平均大小大于老年代的剩余大小。


三、有没有了解过JVM调优,基本思路是什么?

简单了解过,JVM调优的主要工具有Jconsole和VisualVM。这些工具可以根据JVM中的数据情况绘制一些可视化的图表。JVM调优的主要思路是根据这些可视化的信息观察一段时间内内存的释放情况。

四、如何设计存储海量数据的存储系统

我想借鉴Redis集群的思想说一下我的想法:

1. 分片存储: 所有的数据不可能存放在一个节点上,否则高并发情况下访问压力会很大,可以通过一致性哈希算法,将不同的插槽平均得分配到各个节点;

2. 冗余存储: 如果某个节点宕机了,那么这部分数据就会失去或者无法访问,需要建立从数据库对数据进行备份。

3. 故障转移: 在某个主节点发生故障后,从节点应该代替它的位置继续为整个系统提供服务。


五、缓存的实现原理,设计缓存时应该注意什么?

同样借鉴Redis的思想,因为它就是一个缓存的很好的例子。

缓存是为了弥补低速设备和高速设备之间的速度差异而设计的,比如CPU的速度大于内存,内存的速度又大于外存。我们存在数据库中的数据物理上是存在外存中的,对于一些访问频率比较高的数据,把它读到内存中保存一段时间可以提高访问的速度。

设计缓存的时候应该注意:

1. 缓存的大小是有限的,要设计过期策略和淘汰策略(见面经总结N-7/8/9)。

2. 缓存穿透问题: 如果反复查询持久层不存在的数据,就会发生缓存穿透的问题。可以通过缓存空对象和Bloom Filter来解决。
  1) 缓存空对象: 将数据库查询结果为空的key也存储在缓存中。当后续又出现该 key的查询请求时,缓存直接返回null,而无需查询数据库。

  2) Bloom Filter: 将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定 不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。

3. 缓存雪崩: 缓存服务器宕机之后失去了缓存屏障,所有的请求都会涌入持久层。可以通过创建集群和主从缓存服务器来提高缓存系统的可用性。

4. 热点数据集中失效: 大量的Key在同一时间过期,删除大量键值会占用CPU,且大量的请求会涌入持久层。可以在为Key设置过期时间时加上一个随机数


六、某宝热门商品信息存在JVM内存的哪个区域?

一般情况下,商品信息的对象存在堆内存中,如果被序列化成了JSON字符串,也存在堆里,但是如果特别常用的话,可以通过intern方法,将它放入常量池中。


七、操作系统的页式存储管理

页式存储是将程序按照固定的大小分成很多页,在内存有限的情况下,比较大的程序不可能一次性全部装入内存去运行,就需要动态得将需要执行的页调入到内存中。

逻辑地址到物理地址的映射: 根据虚拟页号查找内存中的页表,映射得到物理的页号,如果查不到,就是发生了缺页,系统此时会发生中断,然后从外存中调入所需的页。如果内存空间不足,这个时候还涉及到淘汰机制,常见的有FIFO、LRU等算法。


八、volatile如何保证内存的可见性?

  • 当一个线程要对被volatile修饰的对象进行写操作时,新值会被立即刷新到主内存;
  • 当一个线程要读取被volatile修饰的对象时,它的工作内存会中这个对象的值会被置为不可用,迫使它不得不去主内存中读取这个对象。

这样就保证了内存的可见性。


九、happens-before原则

1. 程序次序规则: 一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;

2. 锁定规则: 一个unLock操作先发生于后面对同一个锁的Lock操作;

3. volatile变量规则: 对一个变量的写操作先行发生于后面对这个变量的读操作;

4. 传递规则: 如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;

5. 线程启动规则: Thread对象的start()方法先行发生于此线程的每一个动作;

6. 线程中断规则: 对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;

7. 线程终结原则: 线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;

8. 对象终结规则: 一个对象的初始化完成先行发生于他的finalize()方法的开始;


十、Java锁有哪些种类,以及区别

锁的分类有很多,比如:

1. 乐观锁和悲观锁: 悲观锁是遇到并发就加锁,乐观锁也称为无锁,最常见的实现是Compare And Set。悲观锁是阻塞式的,并发情况下,乐观锁的性能会比悲观锁好。

2. 公平锁和非公平锁: 公平锁就是每个线程根据申请锁的时间顺序来获取锁,而非公平锁是上一个线程释放锁后,会使其他的线程随机获得锁。

3. 共享锁和独占锁: 共享锁又称读锁,独占锁又称写锁,当一个线程对一个对象上了读锁后,其他的线程只能对这个对象上读锁,不能上写锁。当一个线程对一个对象上了写锁之后,其他线程既不能上写锁,也不能上读锁。


十一、Http请求的过程与原理

Http请求是通过客户端和服务端建立TCP连接实现的

1. 首先,客户端通过套接字与服务端通过TCP三次握手建立TCP连接;
2. 建立连接之后,客户端向服务端发送请求;
3. 服务端收到请求之后,向客户端返回相应的响应;
4. 然后经过一段keepAlive时间,这段时间内客户端还可以用同一个连接继续向服务端发送请求;
5. keepAlive时间结束后,客户端和服务端通过TCP的四次挥手断开连接。


十二、TCP连接的特点,如何保证连接的安全可靠

TCP是面向连接的安全可靠的传输层协议。

TCP的安全可靠是通过确认重传机制实现的,在滑动窗口协议中,接收窗口会在连续收到的包序列中的最后一个包向接收端发送一个ACK,当网络拥堵的时候,发送端的数据包和接收端的ACK包都有可能丢失。TCP为了保证数据可靠传输,就规定在重传的“时间片”到了以后,如果还没有收到对方的ACK,就重发此包,以避免陷入无限等待中。


十三、为什么建立TCP连接需要三次握手,两次不可以吗?

两次不可以,因为三次握手是为了初始化Sequence的初始值,sequence值标识的是两端传输的报文的序号,保证其不会乱序。


十四、AOP的原理,JDK动态代理与CGLib代理的区别

AOP是面向切面的编程,它作为OOP的补充,适用于具有横切逻辑的场景,如性能监控、事务管理和日志记录。AOP的实现主要依靠Java动态代理。分为JDK动态代理CGLib动态代理两种。

  1) JDK动态代理: 适用于业务逻辑通过接口实现的场景;
  2) CGLib动态代理: 可以不依赖于接口,它可以为被代理类生成一个子类,然后拦截父类的所有方法调用,然后织入增强逻辑。


十五、代理的实现原理

讲一下CGLib代理的实现。

1. 首先创建业务类的代理类,实现MethodInterceptor(方法拦截器)。
2. 具体实现intercept函数,把增强逻辑织入被代理方法中。
3. 调用的时候通过代理类的实例去调用业务函数就可以实现代理。


十六、有没有看过Spring源码,说说Ioc容器的加载过程

1. IoC容器首先通过代码中的各种定义创建Bean定义注册表;

2. 然后Bean需要被调用的时候,通过getBean获取Bean的实例:
  1) 尝试从缓存中加载实例,如果不存在,则
  2) 根据Bean定义注册表实例化Bean;
  3) 然后实例化依赖的Bean;
  4) 最后将Bean放入Bean缓存池中,方便下次调用。


十七、了解过字节码的编译过程吗?

大致了解过,需要经过词法分析,语法分析,语义分析,最后生成字节码。


十八、Session放在哪里,如何保存会话状态,有哪些方式、区别是什么?

Session存在Redis中。保存会话状态可以通过:

答案同如何解决HTTP无状态,见《实战面经(一)》第十题


十九、写一下快排的代码

    public static void quickSort(int[] nums, int left, int right){
        int l = left, r = right, key = nums[left];
        while (l < r){
            while (l < r && nums[r] >= key)
                r--;
            nums[l] = nums[r];
            while (l < r && nums[l] <= key)
                l++;
            nums[r] = nums[l];
        }
        nums[l] = key;
        if (left < l)
            quickSort(nums, left, l - 1);
        if (r < right)
            quickSort(nums, r + 1, right);
	}

二十、了解哪些设计模式,举例说说在JDK源码哪些用到了这些设计模式

1. 单例模式: 一个类同一时间只存在一个它的实例,且这个实例由这个类自己来维护,JDK中的Runtime类就用到了单例模式;

2. 工厂方法模式: 让工厂类的子类决定应该实例化那个类。JDK中的线程池就用到了工厂方法模式;

3. 代理模式: 在不改写被代理类的前提下为被代理类实现更多的业务逻辑。JDK动态代理就用到了代理模式。


后记

这篇面经里的知识点考的也不少,部分前面的博文中出现过的题目我们采取了链接的方式,跳转过去看,这样避免了冗余,方便加深记忆,且易于维护和修改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IMplementist

你的鼓励,是我继续写文章的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值