1、线程池的核心参数与工作原理
线程池的核心是 ThreadPoolExecutor,有 7 个参数:核心线程数、最大线程数、空闲存活时间、时间单位、阻塞队列、线程工厂、拒绝策略。
执行流程是:任务提交后,如果核心线程未满,创建核心线程执行;如果核心线程满了,放入阻塞队列;如果队列满了,创建非核心线程;如果达到最大线程数,触发拒绝策略。
2、synchronized 和 ReentrantLock 的区别
synchronized 是 JVM 层面的关键字,自动加锁释放锁;ReentrantLock 是 JDK 层面的 API,需要手动 lock() / unlock()(必须放在 finally)。
ReentrantLock 更灵活:支持公平锁、可中断(lockInterruptibly)、超时获取(tryLock)、多条件变量(Condition)
3、Jvm垃圾回收的流程
JVM 的 GC 过程分三步:
第一步,找垃圾:用可达性分析,从 GC Roots 出发,找不到引用链的对象就是垃圾。
第二步,分代:堆分新生代和老年代。新生代用复制算法,因为存活对象少;老年代用标记-整理,因为存活率高。
第三步,回收:新生代 Eden 满了触发 Minor GC,存活对象复制到 Survivor,年龄够了晋升老年代。老年代满了触发 Full GC,会 STW。
回收器从 Serial 演进到 CMS,再到 G1 和 ZGC,核心目标是降低 STW 时间。现在生产环境主流是 G1,通过分 Region 和可预测停顿模型,把停顿控制在几百毫秒内。”
CMS 的四个阶段(只针对老年代)
| 阶段 | 是否 STW | 做什么 |
|---|---|---|
| 初始标记 | STW | 标记 GC Roots 直接可达的老年代对象 |
| 并发标记 | 并发 | 从初始标记对象出发,遍历老年代对象图 |
| 重新标记 | STW | 修正并发标记期间因用户线程修改引用导致的变动(用增量更新 Incremental Update) |
| 并发清除 | 并发 | 清除未被标记的对象,不移动存活对象 |
G1 的四个阶段(针对全堆)
| 阶段 | 是否 STW | 做什么 |
|---|---|---|
| 初始标记 | STW | 标记 GC Roots 直接可达的对象(伴随一次 Young GC) |
| 并发标记 | 并发 | 遍历全堆对象图,用 SATB 记录引用变化 |
| 最终标记 | STW | 处理 SATB 队列,完成标记 |
| 筛选回收 | STW | 统计 Region 回收价值,复制存活对象到新 Region,清空旧 Region |
在并发标记过程中,JVM 把对象抽象地分为三种颜色:
| 颜色 | 含义 | 状态 |
|---|---|---|
| 白色 | 尚未被访问的对象 | 默认状态,如果标记结束还是白色,就是垃圾 |
| 灰色 | 自己被访问了,但它的子引用还没扫描完 | 中间状态,正在处理 |
| 黑色 | 自己和它的所有子引用都扫描完了 | 确认存活 |
标记过程:
-
初始时,所有对象都是白色。
-
GC Roots 直接可达的对象变为灰色,放入“待处理队列”。
-
从队列取出灰色对象,扫描它的子引用:
-
子引用是白色 → 变灰色,加入队列。
-
子引用是灰色或黑色 → 不动。
-
-
扫描完所有子引用后,该对象从灰色变为黑色。
-
队列为空时,标记结束。仍然为白色的对象就是垃圾。
“三色标记是并发标记的算法,把对象分为白、灰、黑三种颜色。白色是未访问,灰色是自己访问了但子引用没扫完,黑色是自己和子引用都扫完了。标记结束后,白色对象就是垃圾。
并发标记期间,用户线程可能修改引用,导致漏标。漏标需要同时满足两个条件:黑色对象新增了对白色对象的引用,且灰色对象到该白色对象的引用消失了。
CMS 用增量更新,破坏第一个条件:只要黑色对象新增了对白色对象的引用,就把黑色对象变回灰色,重新标记阶段再扫描一遍。
G1 用 SATB,破坏第二个条件:在并发标记开始时做逻辑快照,如果引用被删除,就把旧引用记录下来,认为它在快照时刻还活着。代价是产生浮动垃圾。
两者都用三色标记,只是解决漏标的策略不同。”
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!
浙公网安备 33010602011771号