面试题(2)

本文详细讲述了JVM中线程共享区、垃圾回收过程、GCRoot概念、垃圾回收算法、STW的理解、线程安全、守护线程、ThreadLocal的作用、并发与并行的区别、Java死锁避免策略、线程池原理、ReentrantLock的公平锁和非公平锁、CountDownLatch与Semaphore差异以及Synchronized与ReentrantLock对比。
摘要由CSDN通过智能技术生成

目录

21.JVM中哪些是线程共享区

22.你们项目如何排查JVM问题

23.⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?

24.怎么确定⼀个对象到底是不是垃圾?

25.GC Root 是什么?

26.JVM有哪些垃圾回收算法?

27.什么是STW?

28.JVM参数有哪些?

29.说说对线程安全的理解

30.对守护线程的理解

31.ThreadLocal是什么?

 32.并发、并行、串行之间的区别

33.Java死锁如何避免?

34.线程池的底层工作原理

35.线程池为什么是先添加列队而不是先创建最大线程?       

 36.ReentrantLock中的公平锁和非公平锁的底层实现     

37.ReentrantLock中tryLock()和lock()⽅法的区别

38.CountDownLatch和Semaphore的区别和底层原理

39.Sychronized的偏向锁、轻量级锁、重量级锁

40.Sychronized和ReentrantLock的区别


21.JVM中哪些是线程共享区


JVM中线程共享区有: 堆和方法区

JVM中线程独有的是:  栈,本地方法栈,程序计数器

22.你们项目如何排查JVM问题


对于还在正常运⾏的系统:
1. 可以使⽤jmap来查看JVM中各个区域的使⽤情况
2. 可以通过jstack来查看线程的运⾏情况,⽐如哪些线程阻塞、是否出现了死锁
3. 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc⽐较频繁,那么就得进⾏ 调优了
4. 通过各个命令的结果,或者 jvisualvm等⼯具来进⾏分析
5. ⾸先,初步猜测频繁发送fullgc的原因,如果频繁发⽣fullgc但是⼜⼀直没有出现内存溢出,那么表 示fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收掉,避免这 些对象进⼊到⽼年代,对于这种情况,就要考虑这些存活时间不⻓的对象是不是⽐较⼤,导致年轻 代放不下,直接进⼊到了⽼年代,尝试加⼤年轻代的⼤⼩,如果改完之后,fullgc减少,则证明修改有效
6. 同时,还可以找到占⽤CPU最多的线程,定位到具体的⽅法,优化这个⽅法的执⾏,看是否能避免 某些对象的创建,从⽽节省内存
对于已经发⽣了OOM的系统:
1. ⼀般⽣产系统中都会设置当系统发⽣了OOM时,⽣成当时的dump⽂件( -
XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base )
2. 我们可以利⽤jsisualvm等⼯具来分析dump⽂件
3. 根据dump⽂件找到异常的实例对象,和异常的线程(占⽤CPU⾼),定位到具体的代码
4. 然后再进⾏详细的分析和调试
总之,调优不是⼀蹴⽽就的,需要分析、推理、实践、总结、再分析,最终定位到具体的问题
 

23.⼀个对象从加载到JVM,再到被GC清除,都经历了什么过程?

  • 首先把字节码文件内容加载到方法区
  • 再根据类信息在堆创建对象
  • 对象首先分配在堆中的年轻代的伊甸园区,经过一次轻GC之后,如果对象存在就进入幸存一区,
  • 在之后经过多次轻GC过后,如果对象一直存活,这个对象就会在幸存一区和幸存二区来回from to,
  • 每一次from to 移动 年龄就会增长1岁
  • 当年龄大于15岁,如果这个对象还存活,就会进入老年区
  • 在老年区时,经过full GC ,如果被标记为垃圾对象,就会被GC线程清除

24.怎么确定⼀个对象到底是不是垃圾?

  • 引用计数器: 这种方式就是给堆内存中的对象进行计数,当计数为0时,就会被认为是垃圾对象。这是早起JDK的方式,无法解决循环引用问题。
  • 可达性分析:从一个根对象 (GC root)向下寻找,找到的就不是垃圾对象,未找到的就是垃圾对象。

25.GC Root 是什么?

GC Root 就是根对象,栈中的局部变量表,方法区中的静态变量,正在运行的线程,本地方法栈中的变量都可以是GCRoot。

26.JVM有哪些垃圾回收算法?

1. 标记清除算法:
        a. 标记阶段:把垃圾内存标记出来
        b. 清除阶段:直接将垃圾内存回收。
        c. 这种算法是⽐较简单的,但是有个很严重的问题,就是会产⽣⼤量的内存碎⽚。
2. 复制算法:为了解决标记清除算法的内存碎⽚问题,就产⽣了复制算法。复制算法将内存分为⼤⼩ 相等的两半,每次只使⽤其中⼀半。垃圾回收时,将当前这⼀块的存活对象全部拷⻉到另⼀半,然 后当前这⼀半内存就可以直接清除。这种算法没有内存碎⽚,但是他的问题就在于浪费空间。⽽ 且,他的效率跟存活对象的个数有关。
3. 标记压缩算法:为了解决复制算法的缺陷,就提出了标记压缩算法。这种算法在标记阶段跟标记清 除算法是⼀样的,但是在完成标记之后,不是直接清理垃圾内存,⽽是将存活对象往⼀端移动,然 后将边界以外的所有内存直接清除。

27.什么是STW?

STW(Stop The World) :在执行垃圾回收时,将JVM冻结的一种状。发生STW时,停止一切Java线交互,本地方法native可以执行,但是不能与JVM进行交互。各种算法的优化就是减少STW。

28.JVM参数有哪些?

JVM参数⼤致可以分为三类:
1. 标准 指令: -开头,这些是所有的HotSpot都⽀持的参数。可以⽤java -help 打印出来。
2. ⾮标准指令: -X开头,这些指令通常是跟特定的HotSpot版本对应的。可以⽤java -X 打印出来。
3. 不稳定参数: -XX 开头,这⼀类参数是跟特定HotSpot版本对应的,并且变化⾮常⼤

29.说说对线程安全的理解

线程安全指的是,我们写的某段代码,在多个线程同时执⾏这段代码时,不会产⽣混乱,依然能够得到 正常的结果,⽐如i++,i初始化值为0,那么两个线程来同时执⾏这⾏代码,如果代码是线程安全的,那么最终的结果应该就是⼀个线程的结果为1,⼀个线程的结果为2,如果出现了两个线程的结果都为1,则 表示这段代码是线程不安全的。
所以线程安全,主要指的是⼀段代码在多个线程同时执⾏的情况下,能否得到正确的结果。

30.对守护线程的理解

线程分为⽤户线程和守护线程,⽤户线程就是普通线程,守护线程就是JVM的后台线程,⽐如垃圾回收 线程就是⼀个守护线程,守护线程会在其他普通线程都停⽌运⾏之后⾃动关闭。我们可以通过设置 thread.setDaemon(true)来把⼀个线程设置为守护线程

31.ThreadLocal是什么?

ThreadLocal ,即线程本地变量。如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是操作自己本地内存里面的变量,从而起到线程隔离的作用,避免了线程安全问题。

 32.并发、并行、串行之间的区别

1. 串⾏:⼀个任务执⾏完,才能执行下⼀个任务
2. 并⾏( Parallelism ):两个任务同时执⾏
3. 并发( Concurrency ):两个任务整体看上去是同时执行,在底层,两个任务被拆成了很多份,然后⼀个⼀个执⾏,站在更⾼的⻆度看来两个任务是同时在执行的

33.Java死锁如何避免?

造成死锁的⼏个原因:
1. ⼀个资源每次只能被⼀个线程使⽤
2. ⼀个线程在阻塞等待某个资源时,不释放已占有资源
3. ⼀个线程已经获得的资源,在未使⽤完之前,不能被强⾏剥夺
4. 若⼲线程形成头尾相接的循环等待资源关系
这是造成死锁必须要达到的4个条件,如果要避免死锁,只需要不满⾜其中某⼀个条件即可。⽽其中前3 个条件是作为锁要符合的条件,所以要避免死锁就需要打破第4个条件,不出现循环等待锁的关系。
在开发过程中:
1. 要注意加锁顺序,保证每个线程按同样的顺序进⾏加锁
2. 要注意加锁时限,可以针对所设置⼀个超时时间
3. 要注意死锁检查,这是⼀种预防机制,确保在第⼀时间发现死锁并进⾏解决

34.线程池的底层工作原理

线程池内部是通过队列+线程实现的,当我们利⽤线程池执⾏任务时:
1. 如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建 新的线程来处理被添加的任务。
2. 如果此时线程池中的线程数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放⼊ 缓冲队列。
3. 如果此时线程池中的线程数量⼤于等于corePoolSize,缓冲队列workQueue满,并且线程池中的数 量⼩于maximumPoolSize,建新的线程来处理被添加的任务。
4. 如果此时线程池中的线程数量⼤于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等 于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
5. 当线程池中的线程数量⼤于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被 终⽌。这样,线程池可以动态的调整池中的线程数

35.线程池为什么是先添加列队而不是先创建最大线程?
       

当线程池中的核⼼线程都在忙时,如果继续往线程池中添加任务,那么任务会先放⼊队列,队列满了之 后,才会新开线程。这就相当于,⼀个公司本来有10个程序员,本来这10个程序员能正常的处理各种需 求,但是随着公司的发展,需求在慢慢的增加,但是⼀开始这些需求只会增加在待开发列表中,然后这 10个程序员加班加点的从待开发列表中获取需求并进⾏处理,但是某⼀天待开发列表满了,公司发现现有的10个程序员是真的处理不过来了,所以就开始新招员⼯了。

 36.ReentrantLock中的公平锁和非公平锁的底层实现
     

⾸先不管是公平锁和⾮公平锁,它们的底层实现都会使⽤AQS来进⾏排队,它们的区别在于:线程在使 ⽤lock()⽅法加锁时,如果是公平锁,会先检查AQS队列中是否存在线程在排队,如果有线程在排队, 则当前线程也进⾏排队,如果是⾮公平锁,则不会去检查是否有线程在排队,⽽是直接竞争锁。 不管是公平锁还是⾮公平锁,⼀旦没竞争到锁,都会进⾏排队,当锁释放时,都是唤醒排在最前⾯的线 程,所以⾮公平锁只是体现在了线程加锁阶段,⽽没有体现在线程被唤醒阶段。
另外,ReentrantLock是可重⼊锁,不管是公平锁还是⾮公平锁都是可重⼊的

37.ReentrantLock中tryLock()和lock()⽅法的区别

1. tryLock()表示尝试加锁,可能加到,也可能加不到,该⽅法不会阻塞线程,如果加到锁则返回
true,没有加到则返回false
2. lock()表示阻塞加锁,线程会阻塞直到加到锁,⽅法也没有返回值

38.CountDownLatch和Semaphore的区别和底层原理

  • CountDownLatch表示计数器,可以给CountDownLatch设置⼀个数字,⼀个线程调⽤
  • CountDownLatch的await()将会阻塞,其他线程可以调⽤CountDownLatch的countDown()⽅法来对 CountDownLatch中的数字减⼀,当数字被减成0后,所有await的线程都将被唤醒。
  • 对应的底层原理就是,调⽤await()⽅法的线程会利⽤AQS排队,⼀旦数字被减为0,则会将AQS中 排队的线程依次唤醒。
  • Semaphore表示信号量,可以设置许可的个数,表示同时允许最多多少个线程使⽤该信号量,通 过acquire()来获取许可,如果没有许可可⽤则线程阻塞,并通过AQS来排队,可以通过release()
  • ReentrantLock中tryLock()和lock()⽅法的区别
  • CountDownLatch和Semaphore的区别和底层原理⽅法来释放许可,当某个线程释放了某个许可后,会从AQS中正在排队的第⼀个线程开始依次唤 醒,直到没有空闲许可。

39.Sychronized的偏向锁、轻量级锁、重量级锁

1. 偏向锁:在锁对象的对象头中记录⼀下当前获取到该锁的线程ID,该线程下次如果⼜来获取该锁就 可以直接获取到了
2. 轻量级锁:由偏向锁升级⽽来,当⼀个线程获取到锁后,此时这把锁是偏向锁,此时如果有第⼆个 线程来竞争锁,偏向锁就会升级为轻量级锁,之所以叫轻量级锁,是为了和重量级锁区分开来,轻 量级锁底层是通过⾃旋来实现的,并不会阻塞线程
3. 如果⾃旋次数过多仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞
4. ⾃旋锁:⾃旋锁就是线程在获取锁的过程中,不会去阻塞线程,也就⽆所谓唤醒线程,阻塞和唤醒 这两个步骤都是需要操作系统去进⾏的,⽐较消耗时间,⾃旋锁是线程通过CAS获取预期的⼀个标 记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程⼀直在运 ⾏中,相对⽽⾔没有使⽤太多的操作系统资源,⽐较轻量。

40.Sychronized和ReentrantLock的区别

  • sychronized是⼀个关键字,ReentrantLock是⼀个类
  • sychronized会⾃动的加锁与释放锁,ReentrantLock需要程序员⼿动加锁与释放锁
  • sychronized的底层是JVM层⾯的锁,ReentrantLock是API层⾯的锁
  • sychronized是⾮公平锁,ReentrantLock可以选择公平锁或⾮公平锁
  • sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识 来标识锁的状态
  • sychronized底层有⼀个锁升级的过程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值