第一章、Java基础:恕我直言 在座的都是垃圾
第二章、Spring及SpringBoot:不是bean生命周期就是循环依赖,还有念念不忘的starter
第三章、MyBatis:啥是SQL注入?
第四章、SpringCloud及微服务:别在问一致性了
第五章、MySQL:RR级别是咋解决幻读?
第六章、Redis:快、很快、非常快?Why? 因为快啊
第七章、Kafka:其实我只知道道高性能、高吞吐
第八章、算法:除了暴力算法 你还会神马?
第九章、场景设计
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
越是基础 越容易翻车
1、常见GC算法有哪些?
答:首先先搞清楚GC中最基础的算法,不同的垃圾回收器采用不同的基础算法
- 标记 -清除算法 :如它的名字一样,算法分为“标记”和“清 除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。(标记清除算法有它自己的问题存在,我们从根找到了那些在用不可回收的,然后标记出可以回收的,清除之后就变成了空闲的,这种算法相对简单,在存活对象较多的时候效率比较高,这种算法需要扫描两遍,第一遍是找到那些有用的,第二遍是把那些没有用的找出来然后清理掉,执行效率偏低,容易产生碎片)
复制算法:它将可用内存按容量划分为大小相等的两块,每次 只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后 再把已使用过的内存空间一次清理掉;这个算法与标记-整理算法的区别在于,该算法不是在同一个区域复制,而是将所有存活的对象复制到另一个区域内。(非常简单,就是把内容一分为二,把有用的拷贝到没用的一边,然后把剩下的全部清除,适用于存活对象较少的情况,只扫描一次,效率提高了很多而且没有碎片产生,但是浪费空间,移动复制对象要调整对象的引用)
标记-压缩算法(标记-整理):标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行 清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存(标记整理算法依然有他的问题,在此过程中扫描两次而且还需要移动对象,第一次先找出有用的 第二遍才开始移动,移动过程中如果是多线程还需要进行同步,效率上肯定是降低很多,但是不会产生碎片,方便对象分配,不会产生内存减半。)
扩展:这里有个前提就是标记;那么如何标记出,也就是需要首先找到这些需要被回收的对象,八股的答案是2种:
- 已用计数法:有一个引用指向一个对象,计数就加1 ,直到这个数为0,就会被当作垃圾,不能解决一个问题(循环引用),如果根据引用计数法,这些都不是垃圾,可是没有其他引用指向这一团,那他们就是一团垃圾
- 可达性分析法(有人称之为GC roots方法 无所谓了):Tracing GC的根本思路是: 给定一个集合的引用作为根出发, 通过引用关系遍历对象图, 能被遍历到的(可达到的)对象就判定为存活, 其余对象(也就是没有被遍历到的)就自然被判定为死亡。注意再注意: tracing GC的本质是通过找出所有活对象来把其余空间认定为"无用", 而不是找出所有死掉的对象并回收它们占用的空间;
GC Roots这组引用是tracing GC的起点。要实现语义正确的tracing GC, 就必须要能完整枚举出所有的GC roots, 否则就可能会漏扫描应该存活的对象, 导致GC错误回收了这些被漏扫的活对象。
目前主流的虚拟机都是采用GC Roots Tracing算法, 比如Sun的Hotspot虚拟机便是采用该算法, 该算法的核心算法是从GC Roots对象作为起始点, 利用数学中图论知识, 图中可达对象便是存活对象, 而不可达对象则是需要回收的垃圾内存。
GC 管理的区域是Java堆,虚拟机栈、方法区和本地方法栈不被 GC 所管理,因此选用这些区域内引用的对象作为 GC Roots,是不会被 GC 所回收的。其中虚拟机栈和本地方法栈都是线程私有的内存区域,只要线程没有终止,就能确保它们中引用的对象的存活。而方法区中类静态属性引用的对象是显然存活的。常量引用的对象在当前可能存活,因此,也可能是 GC roots 的一部分
那么问题来了?到底哪些对象可以被作为GC Roots对象呢?
虚拟机栈(栈帧中的本地变量表)中引用的对象
本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象这里需要注意一点:GC发生在JMM中的堆和方法区!
方法区:1.7与1.8后有不同的实现方式;1.7的方法区时堆的一个逻辑划分,也就是说和堆内存是连续的,主要用于存储虚拟机加载的类信息、常量、静态变量,以及编译器编译后的代码等数据。为了与堆做区分,方法区还有个名字叫“非堆”,也有人用“永久代;1.8后方法区的实现方式是元空间,是和堆物理上隔离的;原方法区中存储的类信息、编译后的代码数据等已经移动到了元空间(MetaSpace)中,元空间并没有处于堆内存上,而是直接占用的本地内存。
FGC会触发回收方法区(只是回收 但不一定能够回收成功);方法区的回收要苛刻的多,性价比低
方法区垃圾收集主要包括两个部分:
废弃的常量,不再使用的类型
【废弃的常量】
与回收堆对象类似。
例 :常量池字面量回收
如果虚拟机在其他地方没有引用这个常量量,如果发生垃圾回收,并且垃圾收集器判断确实有必要的话,这个常量就会被系统清理出常量池。
【不再使用的类型】
(1)该类的所有的实例都已经被回收
(2)加载该类的类加载器已经被回收
(3)该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类。
java虚拟机允许对满足上面三个条件的类回收,但是对于是否要对类型进行回收,Hotspot虚拟机提供了-Xnoclassgc参数进行控制
这里的GC和对象头有啥关系?
2、你们用的什么垃圾回收器?知道G1吗?有啥优势?
答:ParNew+CSM ;CSM是老年代垃圾回收器,G1回收的是新生代及老年代(G1收集器是JDK1.7提供的一 个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。此外,G1收 集器不同于之前的收集器的一个重要特点是:G1回收的范围是整个Java堆(包括新生代,老年 代),而前六种收集器回收的范围仅限于新生代或老年代。)相较于ParNew+CMS,最大的特点就是会将java的堆内存拆分成多个大小相等的Region。虽然G1也有年轻代和老年代的区分,但也只是在概念上区分。可以理解为G1将一部分的Region划分为年轻代,然后将另一部分划分成了老年代;G1还有一个特点,就是可以让我们设置一个垃圾回收器预期的停顿时间,也就是stop the world时间。比如我们可以设置,在一小时内,G1的stop the world时间不能超过1分钟。看到这里大家应该可以明白G1相较于ParNew+CMS的优化点在哪里了吧。G1优化的主要点就是尽可能的减少MinorGC和FullGC,尽可能的减少GC带来的系统停顿
扩展:G1是如何做到stop the world时间可控的呢
G1之所以能做到回收时间可控,主要是得益于Region这个结构。G1会记录每个Region里的对象有多少是垃圾对象,如果要对某个Region进行垃圾回收,他会计算出对该Region回收的时间,可以回收多少垃圾;在G1中,每一个Region都可能属于新生代,也可能属于老年代。
刚开始的时候,Region可能不属于新生代也不属于老年代,但是过段时间之后,被G1分配给了新生代,然后放了很多新生代的对象。在经历了垃圾回收之后,可能下一次同一个Region又被分配给了老年代,用来存放老年代的长生存周期的对象。
因此,在G1垃圾回收器中,没有给新生代和老年代分配多大内存的设定,他们各自的内存大小是在不停变动的,都是由G1控制。
3、类加载过程?双亲委派机制?可以打破双亲委派机制吗?
答:加载过程:加载--->验证---->准备---->解析---->初始化---->使用---->卸载
其中验证,准备,解析合称链接
加载:通过类的完全限定名,查找此类字节码文件,利用字节码文件创建Class对象.
验证:确保Class文件符合当前虚拟机的要求,不会危害到虚拟机自身安全
准备:进行内存分配,为static修饰的类变量分配内存,并设置初始值(0或null).不包含final修饰的静态变 量,因为final变量在编译时分配.
解析:将常量池中的符号引用替换为直接引用的过程.直接引用为直接指向目标的指针或者相对偏移量 等.
初始化:主要完成静态块执行以及静态变量的赋值.先初始化父类,再初始化当前类.只有对类主动使用 时才会初始化.
触发条件包括,创建类的实例时,访问类的静态方法或静态变量的时候,使用Class.forName反射类的时 候,或者某个子类初始化的时候.
Java自带的加载器加载的类,在虚拟机的生命周期中是不会被卸载的,只有用户自定义的加载器加载的 类才可以被卸.
双亲委派机制:
如果一个类加载器收到了类加载请求,它首先不会自动去尝试加载这个类,而是把这个类委托给父类加载器去完成,每一层依次这样,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成该加载请求(找不到所需的类)时,这个时候子加载器才会尝试自己去加载,这个过程就是双亲委派机制!
扩展:
说到双亲委派机制,首先你要了解,什么是类加载器。
java虚拟机设计团队有意将类加载阶段中“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到java虚拟机外部去实现,以便于让应用程序自己决定如何去获取所需的类。实现这个动作的代码称作为“类加载器(Class Loader)”
------《深入理解JAVA虚拟机》
JVM支持两种加载器,分别为引导类加载器(BootstrapClassLoader)和自定义类加载器。从概念上来说自定义加载器一般是程序中由开发人员定义的一类加载器,然而java虚拟机规范中并没有这样定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义加载器
在java8以及以前的版本都会用到如下三种加载器
- 启动类加载器(Bootstrap Class Loader)
- 扩展类加载器(Extension Class Loader)
- 应用类加载器(Application Class Loader)
启动类加载器(Bootstrap Class Loader)
该加载器使用C++实现(不会继承ClassLoader),是虚拟机自身的一部分。该类加载器主要是负责加载存放在JAVA_HOME\lib目录,或者被-Xbootclasspath参数指定路径存放的,并且是java虚拟机能识别的类库加载到虚拟机内存中。(eg:主要是加载java的核心类库,即加载lib目录下的所有class)
扩展类加载器
这个类加载器主要是负责加载JAVA_HOME\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有类库
应用类加载器
这个类的加载器是由sun.misc.Launcher$AppClassLoader来实现,因为该加载器是ClassLoader类中的getSystemClassLoader()方法的返回值,所以一般也称为该加载器为系统类加载器。该加载器主要是加载用户类路径上所有的类库,如果应用程序中没有定义过自己的类加载器,一般情况下这个就是程序的默认加载器
4、啥是SPI?
5、equals与==的区别
6、instanceof 关键字的作用
7、什么是JIT?
8、final有哪些用法?
9、i=i+1与i+=1有区别吗?
10、什么是反射?有啥作用
11、Java中集合类有哪些?
12、聊下浅拷贝及深拷贝
13、红黑树有哪几个特征?java中有使用到红黑树吗?
14、既然有数组为啥还要ArrayList?
15、Java创建对象的方式有哪些?
答:new创建新对象 通过反射机制 采用clone机制 通过序列化机制
16、不相同的对象有可能有相同的hashcode?怎么破?
17、(八股)HashMap和HashTable的区别?
18、HashMap 与 ConcurrentHashMap 的异同?ConcurrentHashMap实现原理(1.7与1.8区别)?
19、有遇到过OOM吗?咋定位咋解决?SOF呢?
20、什么是内部类?匿名内部类?
21、谈谈类加载过程及卸载
22、说说JVM内存结构?运行时数据区划分
23、对象分配原则
24、JVM加载class文件的原理机制
25、说说Java对象创建过程
26、类的生命周期吗?
27、简述Java的对象结构
答:(这里要和GC、线程锁关联起来)
28、Minor GC与Full GC分别在什么时候发生?
29、对象一定分配在堆中吗?
30、什么是逃逸分析?
31、为啥使用元空(1.8)间替待永久代(1.7)
32、什么是STW?安全点?
33、什么是指针碰撞?
34、什么是空闲列表?
35、什么是TLAB?
36、对象头具体都包含哪些内容?
37、说说堆和栈的区别
38、什么是Java虚拟机?为什么Java被称作是“平台无关的编程语 言”?
39、说说对象分配规则
40、说说Java对象创建过程
41、说说类的生命周期
42、对象什么时候可以被回收?
44、常见的调优命令是?
45、你知道哪些调优工具?
46、Minor GC与Full GC分别在什么时候发生
47、常见的JVM性能调优参数有哪些?
48、说说JVM的主要成部分及其作用?
49、tomcat 类加载机制是什么?
50、聊聊JAVA的反射
51、聊聊JAVA的序列化
52、你知道java中的锁有哪些?
53、JAVA 四中引用类型
1、什么是线程安全?
2、聊聊JAVA中的锁?
3、JAVA线程实现/创建方式有哪些?
4、线程状态/生命周期?
5、sleep与wait区别?
6、start与run区别有什么区别?
7、JAVA后台线程
8、volatile 关键字的作用?volatile 能保证现场安全马?
9、ThreadLocal是什么?有啥作用?有什么潜在问题?
10、线程池原理知道吗?以及核心参数
11、线程池的拒绝策略有哪些?你们用的是哪种?为什么?
12、什么是JMM?为什么需要JMM?
13、为什么要用多线程?
14、说说CyclicBarrier和CountDownLatch的区别?你在实际中是怎么使用的?
15、什么是AQS?Java中哪些地方会用到AQS?
16、有用过Semaphore?作用是什么?
17、Callable和Future区别?适用于哪些场景
18、阻塞队列的实现原理是什么?
19、什么是多线程中的上下文切换?
20、什么是Daemon线程?它有什么作用?
21、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
上述问题只是抛砖引玉,我们在看到这些知识点的时候要有自己的思考,多问几个为什么?而不是单纯的死记硬背,这样很容易被面试官在三追问下挂掉,一定要融会贯通,将零散的知识点变成知识网络,(比如我们都知道JVM运行时内存结构,GC算法背的滚瓜烂熟,面试官很可能给你几行代码,问你对象分配在哪?能被回收吗?是否存在泄漏问题?CMS初始标记是标记具体执行过?spring中是否使用了SPI机制?是否会打破双亲委派机智?mysql 为啥用B+树而不用跳表?这2个看起来结构很类似等)这样在面试过程中才能成竹在胸、有的放矢;另外就是在平时code 的过程中一定要看看源码,多想想为啥如此实现,而不是网上copy奉行拿来主义