前言
我一直比较好奇,java程序员面试会面试什么呢?
在安卓的面试中,其实也有好多java知识点,问的不多,但有些问的深的话,就会整到JVM,JVM内存模型,Java内存模型,指令优化,线程同步,各种xx原理是怎样。
时常,一搜,就是一本《深入理解Java虚拟机》,《Think In Java》。。。
这让我很困惑,不就是java咩,至于这么难吗?面试官该不是想找大学java老师么?
于是,想找一篇简单,通熟易懂,脉络清晰的java面试文章。我去拉勾,看了张雷大兄弟的《32个java面试知识点》,发现java程序员这年头也不好混哪,成为高级Java软工,起码得掌握如下技能树:
图0-1 一颗庞大的Java软工要掌握的知识树我作为大安卓阵营的工程师表示:
在下佩服。
接下来,我会总结一下大神经过200多家公司的分析,给出的java必考知识点。尤其还有一些真题。
大概分为4篇吧。
基础模块
一 计算机基础
图1-1计算机基础,要考的话是考不完的,还是整一整重点知识。
1.1 TCP特点
- 基于链接
- 双工通信
- 可靠传输
- 拥塞控制
- 基于字节流而非报文
1.2 TCP的三次握手
- TCP三次握手的过程是怎样的?
提问:泛洪攻击的原理,怎么防治?
Server端发送了ACK,SYN给Client后,Client端没有回复ACK,导致服务端大量的连接,处于SYN_RECIEV状态。对其他的新的连接请求造成影响。
1.3 TCP四次挥手断开连接过程
图1-2 一张图表示4次握手二者的区别:
四次挥手要等待2个MSL原因:
- 第一要保证TCP协议的全双工链接能够很快关闭
- 第二要保证这次链接中重复的数据段能够从网络中消失,防止端口被重用的时候,可能会产生数据混淆。
问题:
- 为啥是三次握手,不是4次或2次握手?
- 为啥是4次挥手,不跟3次握手一样,也弄3次?
一起回答:无论如何,这个交互流程上可以看出,无论是建立链接还是断开连接,都是需要在两个方向上进行,只不过建连是所有端的
SYN和ACK两个包合并为一次发送,而
断开链接是两个方向的数据发送的停止时间可能是不同的`,所以无法合并SYN,ACK发送,这就是建立链接的时候,必须要三次握手,而断连的时候呢,必须要四次握手。
二 Java语言特性
2.1 最常考的知识点:
-
HashMap/CurrrentHashMap区别和联系
- HashMap不是线程安全的,CurrrentHashMap是线程安全的。
注意:- 使用 EntrySet ,而不是先拿到Key,再get Value,遍历HashMap效率高。
- 并发时,如果是1.7版本的HashMap,访问HashMap内容,容易出现链表死循环问题。
- HashMap内部实现采用Hash数组+链表数据结构,不加锁,CurrrentHashMap内部采用分段锁机制,分成多个Segment, 相当于多个HashTable, 根据key的hashCode存放在不同Segment。上锁的是HashTable, 所以,当key的hashCode不同时,不会上锁。
- HashMap不是线程安全的,CurrrentHashMap是线程安全的。
-
HashMap,CurrentHashMap的java 1.7和java1.8版本有什么区别?
- HashMap 1.7采用array + linklist 数据结构, 1.8采用array + linklist + 红黑树数据结构,查询效率更高
- CurrentHashMap 1.8其中抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性
参考:一文了解HashMap和CurrentHashMap区别
- Java版本的新特性
-
V1.8
- Lambda表达式
- Stream API
- 方法引用
- 接口默认方法
- Metaspace替换PermGen
-
V1.9-1.10
- 模块系统
- 默认G1回收期
- 接口私有方法
- 局部变量推断
- Graal编译器
-
V1.11
- ZGC
- 字符串API增强
- 内建 HTTP Client
-
三 JVM 虚拟机
图 3-1 JVM的知识点3.1 JVM原理
- JVM内存模型
- 线程独占:虚拟机栈,本地方法栈, 程序计数器
- 线程共享:堆,方法区
重要知识点:
以上每个区域的功能要点是什么?
哪些区域是线程共享,哪些是线程独占的?
3.2 JMM内存(区别JVM内存模型)
图 3-3 一个JMM内存模型结构
- 线程独有:工作内存
- 主内存(可以结合vilatile 关键字的作用:
内存同步,禁止指令优化
,一起服用,效果更佳)
3.3 JMM要保证内存变量的一些特性
- 原子性
- 可见性
- 有序性
3.3.1 如何保证上述特点呢?
- 基本数据类型的读写(从主内存中拷贝)
- syncrhonized (原理:对象头锁,ACESS_SYNCHRONIZED, MonitorActor, MonitorExit)
- volatile (强制变量赋值刷新内存,读取从主内存中获取;保证线程可见性)
- happend-before原则
- 程序的顺序执行(一个线程保证串行)
- 加锁规则-解锁要在再次加锁之前。
3.4 类加载过程
加载到内存 -> 验证(文件的格式,元数据验证,字节码,符号引用)
-》 准备阶段(类变量内存)-> 解析 -> 初始化 -> 使用(实例化)
-> 卸载
这个过程,如果细考,非常的长,建议参考 《深入理解java虚拟机》
3.5 类加载器
BootStrap ClassLoader
| |
ExtClassLoader
| |
AppClassLoader
| |
Custom ClassLoader
重点:双亲委派模式
3.6 JVM 内存回收算法
- 年轻代 Eden Survivor1 Survivor2
- 老年代 Tenured
- 永久代 PermGen/Metaspace
各种垃圾回收算法:
- CMS算法:
属于标记清除算法,缺点:内存碎片较多。
- G1算法(jvm1.9):
- ZGC-针对64位系统大内存堆的低延迟垃圾回收算法
- 着色指针
- 读屏障
- 并发处理
- 基于Region
- 内存压缩(整理)
- 一些加分项:
- 编译器优化(如何栈分配减少内存的压力,如何编写适合
内联优化
的代码) - 问题排查经验思路
- JVM调优经验和调优思路
- 了解最新的技术趋势。(ZGC, Graalvm)
- 编译器优化(如何栈分配减少内存的压力,如何编写适合
常问的几个问题:
- 简单描述一下JVM的内存模型,可以和JMM对比着讲?
- 什么情况下会触发FullGC?
- Java类加载器有几种?关系是怎样的?
- 双亲委派机制的加载流程是怎样的,有什么好处?
- 在虚拟机里觉得一个类是不是唯一有两个因素,第一个就是这个类本身,第二个就是加载这个类的类加载 器,如果同一个类由不同的类加载器去加载,在虚拟机看来,这两个类是不同的类。
- 1.8为什么使用Metaspace替换掉PermGen?MetaSpace保存在哪?
- 编译期会对指令做哪些优化?(简述指令重排)
- volatile 可以解决什么问题?如何做到?(防止主内存不同步,阻止指令重新排序)
- 简单描述一下GC的分代回收?
- G1垃圾回收算法和CMS的区别?
- 使用过哪些JVM调试工具,主要分析哪些内容?
JMC, JStack,JMap,MAT。
四 多线程
-
线程的几种状态
-
线程的同步与互斥
- CAS(乐观锁)与ABA
- synchronized实现原理
- (ACC_SYNCHRONIZED) 方法同步
- 代码块同步 monitorenter, monitorexit
- 获取锁的方式,锁优化:偏向锁,轻量级锁,自旋锁
- 对象头有个Monitor对象,包含_EnterList进入线程队列, Owner, _WaitSet等待队列)
https://www.cnblogs.com/paddix/p/5367116.html
- AQS 与 Lock
-
线程池的几种类型
- newFixedThreadPool - 固定线程数,无界队列,适用于任务数量不均匀的场景,对内存压力不敏感,但系统负载比较敏感的场景
- newCachedThreadPool - 无限线程数,适用于对延迟要求较低的任务场景
- newSingleThreadPool - 单个线程的固定线程池,适用于保证一步任务顺序执行的场景
- newScheduledThreadPool - 适用于定期执行任务场景,支持固定频率和固定延时
- newWorkStealingPool - 使用ForkJoinPool,多任务队列的固定并行,任务执行时长不均匀的场景
常问的几个问题,真题汇总:
- 如何实现生产者与消费者模型(锁,信号量,线程通信,阻塞队列)
- 如何理解线程的同步与异步,阻塞与非阻塞?
- 线程池处理任务的流程是怎样的?
- wait和sleep有什么不同?
- wait属于object对象,sleep()属于Thread对象
- wait()会释放对象锁,sleep()不会
- wait()出现在同步块中, sleep()可以在任何对象中
- wait()不需要捕获异常,sleep()需要捕获异常
synchronized
和ReetrantLock
有什么不同?适合用于什么情景?- 读写锁适用于什么情景?ReetrantReadWriteLock如何实现的?
- 线程之间如何通信?
- 保证线程安全的方法有哪些?
- CAS,Lock,ThreadLocal
-
如何尽可能提高多线程并发性能?
- 减少临界区范围
- 使用读写锁
- 使用CopyOnWrite
-
ThreadLocal 的作用是什么?
* 用来隔离线程的数据,让线程的数据不在互相影响。
* 不是用来线程共享数据
- 死锁问题产生的条件,如何避免死锁?
* 资源互斥
* 持有资源并请求
* 非剥夺
* 循环等待
to be continued…