诚意分享:SafePoint与StopTheWorld全解,2024年最新2024Java面试心得

本文讨论了Java开发者在自学过程中提升技能的挑战,特别关注Stop-the-world期间的Safepoint问题,分析了导致停顿过长的原因,如大循环、JIT优化和资源瓶颈,并提供了优化策略,包括代码调整和JVM参数设置。同时,文中还提到了大厂Java面试题库的内容和学习资料的分享。
摘要由CSDN通过智能技术生成

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

template(ChangeBreakpoints) \

template(GetOrSetLocal) \

template(GetCurrentLocation) \

template(ChangeSingleStep) \

template(HeapWalkOperation) \

template(HeapIterateOperation) \

template(ReportJavaOutOfMemory) \

template(JFRCheckpoint) \

template(ShenandoahFullGC) \

template(ShenandoahInitMark) \

template(ShenandoahFinalMarkStartEvac) \

template(ShenandoahInitUpdateRefs) \

template(ShenandoahFinalUpdateRefs) \

template(ShenandoahDegeneratedGC) \

template(Exit) \

template(LinuxDllLoad) \

template(RotateGCLog) \

template(WhiteBoxOperation) \

template(JVMCIResizeCounters) \

template(ClassLoaderStatsOperation) \

template(ClassLoaderHierarchyOperation) \

template(DumpHashtable) \

template(DumpTouchedMethods) \

template(PrintCompileQueue) \

template(PrintClassHierarchy) \

template(ThreadSuspend) \

template(ThreadsSuspendJVMTI) \

template(ICBufferFull) \

template(ScavengeMonitors) \

template(PrintMetadata) \

template(GTestExecuteAtSafepoint) \

template(JFROldObject) \

复制

什么情况会导致 Stop the world 时间过长?

================================

Stop the world 阶段可以简单分为(这段时间内,JVM 都是基于所有线程进入 Safepoint 就 block 的状态):

  1. 某个操作,需要 Stop the world(就是上面提到的哪些情况下会让所有线程进入 SafePoint, 即发生 Stop the world 的那些操作)

  2. 向 Signal Dispatcher 这个 JVM 守护线程发起 Safepoint 同步信号并交给对应的模块执行。

  3. 对应的模块,采集所有线程信息,并对每个线程根据状态做不同的操作以及标记(根据之前源代码那一块的描述,有5种情况)

  4. 所有线程都进入 Safepoint 并 block。

  5. 最需要发起 Stop the world 的操作。

  6. 操作完成,所有线程从 Safepoint 恢复。

基于这些阶段,导致 Stop the world 时间过长的原因有:

  1. 阶段 4 耗时过长,即等待所有线程中的某些线程进入 Safepoint 的时间过长,这个很可能和有 大有界循环与JIT优化 有关,也很可能是 OpenJDK 11 引入的获取调用堆栈的类StackWalker的使用导致的,也可能是系统 CPU 资源问题或者是系统内存脏页过多或者发生 swap 导致的。

  2. 阶段 5 耗时过长,需要看看是哪些操作导致的,例如偏向锁撤销过多, GC时间过长等等,需要想办法减少这些操作消耗的时间,或者直接关闭这些事件(例如关闭偏向锁,关闭 JFR 的 OldObjectSample 事件采集)减少进入,这个和本篇内容无关,这里不赘述。

  3. 阶段2,阶段3耗时过长,由于 Signal Dispatcher 是单线程的,可以看看当时 Signal Dispatcher 这个线程在干什么,可能是 Signal Dispatcher 做其他操作导致的。也可能是系统 CPU 资源问题或者是系统内存脏页过多或者发生 swap 导致的。

大有界循环与 JIT 优化会给 SafePoint 带来哪些问题?

=====================================

已知:只有线程执行到 Safepoint 代码才会知道Thread.intterupted()的最新状态,而不是线程的本地缓存。

我们来看下面一段代码:

static int algorithm(int n) {

int bestSoFar = 0;

for (int i=0; i<n; ++i) {

if (Thread.interrupted()) {

System.out.println(“broken by interrupted”);

break;

}

//增加pow计算,增加计算量,防止循环执行不超过1s就结束了

bestSoFar = (int) Math.pow(i, 0.3);

}

return bestSoFar;

}

public static void main(String[] args) throws InterruptedException {

Runnable task = () -> {

Instant start = Instant.now();

int bestSoFar = algorithm(1000000000);

double durationInMillis = Duration.between(start, Instant.now()).toMillis();

System.out.println("after “+durationInMillis+” ms, the result is "+bestSoFar);

};

//延迟1ms之后interrupt

Thread t = new Thread(task);

t.start();

Thread.sleep(1);

t.interrupt();

//延迟10ms之后interrupt

t = new Thread(task);

t.start();

Thread.sleep(10);

t.interrupt();

//延迟100ms之后interrupt

t = new Thread(task);

t.start();

Thread.sleep(100);

t.interrupt();

//延迟1s之后interrupt

//这时候 algorithm 里面的for循环调用次数应该足够了,会发生代码即时编译优化并 OSR

t = new Thread(task);

t.start();

Thread.sleep(1000);

//发现线程这次不会对 interrupt 有反应了

t.interrupt();

}

复制

之后利用 JVM 参数 -Xlog:jit+compilation=debug:file=jit_compile%t.log:uptime,level,tags:filecount=10,filesize=100M 打印 JIT 编译日志到另一个文件,便于观察。最后控制台输出:

broken by interrupted

broken by interrupted

after 10.0 ms, the result is 27

after 1.0 ms, the result is 10

broken by interrupted

after 99.0 ms, the result is 69

after 29114.0 ms, the result is 501

复制

可以看出,最后一次循环直接运行结束了,并没有看到线程已经 interrupted 了。并且 JIT 编译日志可以看到,在最后一线程执行循环的时候发生了发生代码即时编译优化并 OSR:

[0.782s][debug][jit,compilation] 460 % 3 com.test.TypeTest::algorithm @ 4 (44 bytes)

[0.784s][debug][jit,compilation] 468 3 com.test.TypeTest::algorithm (44 bytes)

[0.794s][debug][jit,compilation] 486 % 4 com.test.TypeTest::algorithm @ 4 (44 bytes)

[0.797s][debug][jit,compilation] 460 % 3 com.test.TypeTest::algorithm @ 4 (44 bytes) made not entrant

[0.799s][debug][jit,compilation] 503 4 com.test.TypeTest::algorithm (44 bytes)

复制

3 还有 4 表示编译级别,% 表示是 OSR 栈上替换方法,也就是 for 循环还在执行的时候,进行了执行代码的机器码替换。在这之后,线程就看不到线程已经 interrupted 了,这说明,** JIT 优化后的代码,for 循环里面的 Safepoint 会被拿掉**。

这样带来的问题,也显而易见了,当需要 Stop the world 的时候,所有线程都会等着这个循环执行完,因为这个线程只有执行完这个大循环,才能进入 Safepoint。

那么,如何优化呢?

第一种方式是修改代码,将 for int 的循环变成 for long 类型:

for (long i=0; i<n; ++i) {

if (Thread.interrupted()) {

System.out.println(“broken by interrupted”);

break;

}

//增加pow计算,增加计算量,防止循环执行不超过1s就结束了

bestSoFar = (int) Math.pow(i, 0.3);

}

复制

第二种是通过 -XX:+UseCountedLoopSafepoints 参数,让 JIT 优化代码的时候,不会拿掉有界循环里面的 SafePoint

用这两种方式其中一种之后的控制台输出:

broken by interrupted

broken by interrupted

after 0.0 ms, the result is 0

after 10.0 ms, the result is 29

broken by interrupted

after 100.0 ms, the result is 73

broken by interrupted

after 998.0 ms, the result is 170

复制

如何通过日志分析 SafePoint?

=======================

目前,在 OpenJDK 11 版本,主要有两种 SafePoint 相关的日志。一种基本上只在开发时使用,另一种可以在线上使用持续采集。

第一个是 -XX:+PrintSafepointStatistics -XX:PrintSafepointStatisticsCount=1 ,这个会定时采集,但是采集的时候会触发所有线程进入 Safepoint,所以,线程一般不打开(之前我们对于定时让所有线程进入 Safepoint 都要关闭,这个就更不可能打开了)。并且,在 Java 12 中已经被移除,并且接下来的日志配置基本上可以替代这个,所以这里我们就不赘述这个了。

另外是通过 -Xlog:safepoint=trace:stdout:utctime,level,tags ,对于 OpenJDK 的日志配置,可以参考我的另一篇文章详细解析配置的格式,这里我们直接用。

我们这里配置了所有的 safepoint 相关的 JVM 日志都输出到控制台,一次 Stop the world 的时候,就会像下面这样输出:

[2020-07-14T07:08:26.197+0000][debug][safepoint] Safepoint synchronization initiated. (112 threads)

[2020-07-14T07:08:26.197+0000][info ][safepoint] Application time: 12.4565068 seconds

[2020-07-14T07:08:26.197+0000][trace][safepoint] Setting thread local yield flag for threads

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c7c494b30 [0x61dc] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c7c497f30 [0x4ff8] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

…省略一些处于 _at_poll_safepoint 的线程

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c10c010b0 [0x5878] State: _call_back _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.348+0000][trace][safepoint] Thread: 0x0000022c10bfe560 [0x5038] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.197+0000][debug][safepoint] Waiting for 1 thread(s) to block

[2020-07-14T07:08:29.348+0000][info ][safepoint] Entering safepoint region: G1CollectForAllocation

[2020-07-14T07:08:29.350+0000][info ][safepoint] Leaving safepoint region

[2020-07-14T07:08:29.350+0000][info ][safepoint] Total time for which application threads were stopped: 3.1499371 seconds, Stopping threads took: 3.1467255 seconds

复制

首先,阶段 1 会打印日志,这个是 debug 级别的,代表要开始全局所有线程 Safepoint 了,这时候,JVM 就开始无法响应请求了,也就是 Stop the world 开始:

[2020-07-14T07:08:29.347+0000][debug][safepoint] Safepoint synchronization initiated. (112 threads)

复制

阶段 2 不会打印日志,阶段 3 会打印:

[2020-07-14T07:08:26.197+0000][info ][safepoint] Application time: 12.4565068 seconds

[2020-07-14T07:08:26.197+0000][trace][safepoint] Setting thread local yield flag for threads

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c7c494b30 [0x61dc] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c7c497f30 [0x4ff8] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

…省略一些处于 _at_poll_safepoint 的线程

[2020-07-14T07:08:26.197+0000][trace][safepoint] Thread: 0x0000022c10c010b0 [0x5878] State: _call_back _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.348+0000][trace][safepoint] Thread: 0x0000022c10bfe560 [0x5038] State: _at_safepoint _has_called_back 0 _at_poll_safepoint 0

[2020-07-14T07:08:26.197+0000][debug][safepoint] Waiting for 1 thread(s) to block

复制

Application time: 12.4565068 seconds 代表上次全局 Safepoint 与这次 Safepoint 间隔了多长时间。后面 trace 的日志表示每个线程的状态,其中没有处于 Safepoint 的只有一个:

Thread: 0x0000022c10c010b0 [0x5878] State: _call_back _has_called_back 0 _at_poll_safepoint 0

复制

这里有详细的线程号,可以通过 jstack 知道这个线程是干啥的。

最后的Waiting for 1 thread(s) to block也代表到底需要等待几个线程走到 Safepoint。

阶段 4 执行完,开始阶段 5 的时候,会打印:

[2020-07-14T07:08:29.348+0000][info ][safepoint] Entering safepoint region: G1CollectForAllocation

复制

阶段 5 执行完之后,会打印:

[2020-07-14T07:08:29.350+0000][info ][safepoint] Leaving safepoint region

复制

最后阶段 6 开始的时候,会打印:

[2020-07-14T07:08:29.350+0000][info ][safepoint] Total time for which application threads were stopped: 3.1499371 seconds, Stopping threads took: 3.1467255 seconds

复制

Total time for which application threads were stopped是这次阶段1到阶段6开始,一共过了多长时间,也就是 Stop the world 多长时间。后面的Stopping threads took是这次等待线程走进 Safepoint 过了多长时间,一般除了 阶段 5 执行触发 Stop the world 以外,都是由于 等待线程走进 Safepoint 时间长。这时就要看 trace 的线程哪些没有处于 Safepoint,看他们干了什么,是否有大循环,或者是使用了StackWalker这个类.

如何通过 JFR 分析 SafePoint?

==========================

一线互联网大厂Java核心面试题库

image

正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
SafePoint?**

==========================

一线互联网大厂Java核心面试题库

[外链图片转存中…(img-C6vWtbWU-1713549569891)]

正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-BfVNB1rP-1713549569891)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值