Skip to content

1、如何判断对象可以被回收

判断对象是否存活一般有两种方式:

引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。

可达性分析(Reachability Analysis):从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,不可达对象。

2、sleep方法和wait方法有什么区别?

这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器

3、JVM 提供的常用工具

jps:

用来显示本地的 Java 进程,可以查看本地运行着几个 Java 程序,并显示他们的进程号。 命令格式:jps

jinfo:

运行环境参数:Java System 属性和 JVM 命令行参数,Java class path 等信息。 命令格式:jinfo 进程 pid

jstat:

监视虚拟机各种运行状态信息的命令行工具。 命令格式:jstat -gc 123 250 20

jstack:

可以观察到 JVM 中当前所有线程的运行情况和线程当前状态。 命令格式:jstack 进程 pid

jmap:

观察运行中的 JVM 物理内存的占用情况(如:产生哪些对象,及其数量)。 命令格式:jmap [option] pid

4、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

悲观锁:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁。

乐观锁:

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。在 Java中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。

5、生产环境 CPU 占用过高,你如何解决?

1、 top + H 指令找出占用 CPU 最高的进程的 pid

2、 top -H -p

在该进程中找到,哪些线程占用的 CPU 最高的线程,记录下 tid

3、 jstack -l

threads.txt,导出进程的线程栈信息到文本,导出出现异常的话,加上 -F 参数

4、 将 tid 转换为十六进制,在 threads.txt 中搜索,查到对应的线程代码执行栈,在代码中查找占 CPU 比较高的原因。其中 tid 转十六进制,可以借助 Linux 的 printf "%x" tid 指令

我用上述方法查到过,jvm 多条线程疯狂 full gc 导致的CPU 100% 的问题和 JDK1.6 HashMap 并发 put 导致线程 CPU 100% 的问题

6、如何找到死锁的线程?

死锁的线程可以使用 jstack 指令 dump 出 JVM 的线程信息。

jstack -l <pidthreads.txt

有时候需要dump出现异常,可以加上 -F 指令,强制导出

jstack -F -l <pidthreads.txt

如果存在死锁,一般在文件最后会提示找到 deadlock 的数量与线程信息

7、多线程的价值?

1、 发挥多核CPU的优势

多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的,采用多线程的方式去同时完成几件事情而不互相干扰。

2、 防止阻塞

从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。

3、 便于建模

这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。

8、常用并发列队的介绍:

9、线程池的优点?

1、 重用存在的线程,减少对象创建销毁的开销。

2、 可有效的控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

3、 提供定时执行、定期执行、单线程、并发数控制等功能。

10、ZGC 了解吗?

JDK11 中加入的具有实验性质的低延迟垃圾收集器,目标是尽可能在不影响吞吐量的前提下,实现在任意堆内存大小都可以把停顿时间限制在 10ms 以内的低延迟。

基于 Region 内存布局,不设分代,使用了读屏障、染色指针和内存多重映射等技术实现可并发的标记-整理,以低延迟为首要目标。

ZGC 的 Region 具有动态性,是动态创建和销毁的,并且容量大小也是动态变化的。

11、Parallel Scavenge 收集器(多线程复制算法、高效)

12、调优工具

13、什么是指令重排序?

14、说一下 JVM 调优的工具?

15、线程池都有哪些状态?

16、有什么堆外内存的排查思路?

17、当一个线程进入一个对象的 synchronized 方法 A 之后,其它线程是否可进入此对象的 synchronized 方法 B?

18、62、volatile 变量和 atomic 变量有什么不同?

19、什么是CPU密集

20、用Java实现阻塞队列

21、什么是分布式垃圾回收(DGC)?它是如何工作的?

22、什么是 FutureTask

23、串行(serial)收集器和吞吐量(throughput)收集器的区别是什么?

24、Java 内存分配

25、Java 中 ConcurrentHashMap 的并发度是什么?

26、如何在两个线程间共享数据?

27、JVM新生代中为什么要分为Eden和Survivor?

28、有哪些打破了双亲委托机制的案例?

29、GC 是什么?为什么要有 GC?

30、在Java中CycliBarriar和CountdownLatch有什么区别?

31、单例模式的线程安全性

32、32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?

用心去做高质量的内容网站,欢迎 star ⭐ 让更多人发现