真是绝了!这段被JVM动了手脚的代码!

没有发生GC也进入了安全点?这段关于安全点的JVM源码有点意思 熟知并发编程的你认为下面这段代码的执行结果是怎么样的? image 我如果说,执行流程是: t1 线程和 t2 线程一直执行 num 的累加操作 主线程睡眠 1 秒,1 秒之后醒过来打印此时的 num 值 t1 线程和 t2 线程继续执行加 1 的操作,直到执行完2亿次累加操作 你赞成吗? 我的猜想看起来没什么问题,但实际运行效果证明了我是错的,下面是运行动图: image 从运行动图上可以看到,将代码跑起来之后,却发现实际执行结果是这样的: 1 秒之后,主线程并没有马.. 阅读详情

昨天在朋友圈看到了一个号主朋友 glongone 分享了自己的一篇文章。

我点进去看了一下,发现文章中提到的问题真的很有意思。

于是决定转过来一下,分享给大家。

需要说明的是,在不改变原文章想要表达的东西的前提下,我对文章进行了稍微的调整。

比如排版、措辞和一些行文风格,然后充实了一点内容进来。

我们先看原文。

原文

最近在研究多线程,期间写了这么一段代码

public class MainTest {

    public static AtomicInteger num = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable=()->{
            for (int i = 0; i < 1000000000; i++) {
                num.getAndAdd(1);
            }
        };

        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        t1.start();
        t2.start();
        Thread.sleep(1000);
        System.out.println("num = " + num);
    }
}

开始,我是想用原子类去测试线程安全,毫无疑问 CAS 操作是安全的,但是由于这里计算很耗时所以我就让它休眠 1000ms。

然而奇妙的事情发生了,按照代码来看,主线程休眠 1000ms 后就会输出结果,但是实际情况确实主线程一直等待 t1,t2 执行结束才继续执行。

执行结果是这样的:

图片

出于好奇心,我将休眠的时间调整到了100ms,再运行一下,这次运行就正常了,主线程并不会等待子线程运行结束之后在输出。

图片

于是我猜测肯定有什么机制导致了这样的现象,难道是 CAS?

为了验证我将代码改造了一下(注释第5、11行,加入第6、12行):

图片

但是当我运行起来的那一刻我就想起来了:这肯定会直接输出的。

果然结果如我所料。

可见并不是cas引起的总线风暴问题。

有的朋友可能就会问了?

啥是总线风暴呀?

总线风暴:volatile 和CAS 的操作导致BUS总线缓存一致性流量激增所造成

那么...

根据原子类的设计,我只能将瞄头进一步对准 volatile 字段了。

于是乎第三版代码产生了,用 volatile 指定可见性,防止指令重排序,让 sum 的修改对所有线程都可见:

图片

这一次没有失望。果然,通过 volatile 指定对线程的可见性之后, main 方法在 1000ms 以后并没有结束,深度睡眠问题又一次的复现了!

于是我想监控一下运行期间发生了什么,通过 jvisualvm 看一下:

图片

通过监控看到运行期间 CPU 十分繁忙,我电脑是 6 核 12 线程的,CPU 运行期间利用率在 20% 左右。

dump 下来,看了一下,里面有这么一段:

"VM Thread" os_prio=31 tid=0x00007fca6a018000 nid=0x4c03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fca6b009000 nid=0x1e07 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fca69009000 nid=0x2203 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fca6b009800 nid=0x2003 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fca68009000 nid=0x2a03 runnable
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fca6800d000 nid=0x2b03 runnable
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fca6800e000 nid=0x2c03 runnable
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fca6800e800 nid=0x2d03 runnable
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fca6b00a000 nid=0x2e03 runnable
"GC task thread#8 (ParallelGC)" os_prio=31 tid=0x00007fca6800f000 nid=0x4f03 runnable
"GC task thread#9 (ParallelGC)" os_prio=31 tid=0x00007fca6b00a800 nid=0x4d03 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fca6a019000 nid=0xa703 waiting on condition

这...难道是...触发了 GC ???

怎么感觉越陷越深呢?代码中并没有频繁的创建对象,为何会引起 GC ?

这使我更加疑惑了。

百般无奈之下我只能去 stackoverflow 上提个 question 了:

https://stackoverflow.com/questions/67068057/the-main-thread-exceeds-the-set-sleep-time

图片

在等待回答期间,我继续寻找答案。

期间看了这么一篇文章:

  • https://zhuanlan.zhihu.com/p/286110609

图片

作者详细的介绍了由于 GC 导致的  STW 使得线程需要等待一段时间。

HotSpot 定位 GC Roots 时通过引入 Safepoint (安全点)来提高垃圾收集效率。

其中提到:

  • 安全点位置的选取基本上是以“是否具有让程序长时间执行的特征”为标准进行选定的。

  • HotSpot会在所有方法的临返回之前,以及所有非counted loop的循环的回跳之前放置安全点。

  • “长时间执行”的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。

而为什么把这些位置设置为jvm的安全点呢?

主要目的就是避免程序长时间无法进入 safepoint ,比如 JVM 在做 GC 之前要等所有的应用线程进入到安全点后 VM 线程才能分派 GC 任务。如果有线程一直没有进入到安全点,就会导致 GC 时 JVM 停顿时间延长。

毫无疑问我们程序中的循环是十分耗时的。

文章中还有提到安全区域用来解决线程处于 sleep 状态时无法相应虚拟机的终端请求,不能再走到安全的地方去中断挂起自己的问题。

更多的内容大家自己去看一下。

上面的作者的例子中是由于大量创建对象导致  GC 触发的 main 线程等待子线程到达安全点之后执行的效果。

显然和我的场景不完全一样,因为上面的例子中很显然我们是没有触发 GC 的。

但是隐隐约约之间又有着千丝万缕的联系。

图片

终于在 5 小时候我终于等到了 stackoverflow 的回答。这个回答惊艳到了我,不多bb,马上分享一下解惑大佬的说法:

图片

作者的回答从背景,到我写的实例产生的问题,以及具体的解决方案一一道来。

我用大白话给大家简单的翻译一下:

说白了,这是 HotSpot 安全点机制。

先说背景(Background)

通常,HotSpot JVM 会在循环中添加一个safepoint 轮询,以便在 JVM 需要执行 stop the world 操作时暂停线程。

safepoint 轮询不是免费的(也就是说,它有一些性能开销)。

因此 JIT 编译器会在可能的情况下尝试消除它。

其中一个优化是从 counted loops中删除 safepoint 轮询。

那么什么是 counted loops 呢?

你程序里面的 for(int i=0;i<1000000000;i++ 是一个典型的 counted loops:

就是有明确的循环计数器变量,而且该变量有明确的起始值、终止值、步进长度的循环

当我们使用 JDK8 JIT 编译这样的循环时没有 safepoint 轮询。

但是,这是一个非常长的循环,长到需要几秒钟才能完成,所以当这个循环运行时,JVM将无法停止线程。

HotSpot JVM不仅在 GC 的时候使用 safepoints,还有许多其他操作也使用 safepoints。

特别是当有清理任务要做时,它会周期性地停止 Java 线程。

周期由 -XX:GuaranteedSafepointInterval 选项控制,该选项默认为 1000ms。

图片

那么在你的代码示例中发生了什么事情呢(What happens in your example)?

  • 1.你启动了两个长的、不间断的循环(内部没有安全点检查)。

  • 2.主线程进入睡眠状态 1 秒钟。

  • 3.在1000 ms(GuaranteedSafepointInterval)之后,JVM尝试在安全点停止,以便Java线程进行定期清理,但是直到计数循环完成后才能执行此操作。

  • 5.Thread.sleep 方法从 native 返回,发现安全点操作正在进行中,于是把自己挂起,直到操作结束。

所以,从你的角度看起来、你观察到的就是:主线程正在等待循环完成。

如果你想要保留 1000ms 的睡眠代码,那么就增加 JVM 参数 -XX:GuaranteedSafepointInterval=2000,则主线程也不必等待。

接下来我们聊聊修复的事儿。(The fix)

-XX:+UseCountedLoopSafepoints 选项可以关闭安全点轮询的优化。

在这种情况下,程序会按照你的预期去执行。

另外,如果你将 int i 更改为 long i ,循环将不再被视为 counted loop,因此你将不会看到前面说的 safepoint 导致的现象。

自从 JDK10 以来,HotSpot 实现 Loop Strip Mining  优化,解决了在 counted loop 中安全点轮询的问题,而且没有太多开销。

因此,在 JDK10 和更高版本中,你的程序将会按照预期的执行。

对于这个问题和解决办法还有更好的解释,可以在这个问题的描述中找到:

  • https://bugs.openjdk.java.net/browse/JDK-8223051

至此,疑惑解除,简而言之就是一句话:

当 sleep 1000ms 的时候,jvm 尝试在安全点停止 Java 线程以进行定期清理,此时安全点需要一直等待子线程都执行结束后才能继续执行。

其他案例分享

上面就是原文的全部内容。

但是当why哥看到这篇文章的时候,其实电光火石之间,我想到了《深入理解Java虚拟机(第三版)》里面的一个案例。

图片

而周叔叔的这个案例是借鉴的这个链接:

https://juejin.cn/post/6844903878765314061

你可以直接看这个链接内容。我也把第三版 5.2.8 小节搬过来,给大家看看。看完之后,你就知道了,其实他们说的是一回事,案例如下:

有一个比较大的承担公共计算任务的离线 HBase 集群,运行在 JDK 8 上,使用 G1 收集器。

每天都有大量的 MapReduce 或 Spark 离线分析任务对其进行访问,同时有很多其他在线集群 Replication 过来的数据写入。

因为集群读写压力较大,而离线分析任务对延迟又不会特别敏感,所以将 -XX:MaxGCPauseMillis(最大暂停时间)参数设置到了 500ms。

不过运行一段时间后发现垃圾收集的停顿经常达到 3 秒以上,而且实际垃圾收集器进行回收的动作就只占其中的几百毫秒。

图片

日志显示这次垃圾收集一共花费了 0.14 秒,但其中用户线程却足足停顿了有 2.26 秒,两者差距已经远远超出了正常的 TTSP(Time To Safepoint)耗时的范畴。

分析:先加入参数 -XX:+PrintSafepointStatistics 和 -XX:PrintSafepointStatisticsCount=1 去查看安全点日志,具体如下所示:

图片

日志显示当前虚拟机的操作(VM Operation,VMOP)是等待所有用户线程进入到安全点,但是有两个线程特别慢,导致发生了很长时间的自旋等待。

日志中的 2255 毫秒自旋(Spin)时间就是指由于部分线程已经走到了安全点,但还有一些特别慢的线程并没有到,所以垃圾收集线程无法开始工作,只能空转(自旋)等待。

怎么解决这个问题呢?

第一步是把这两个特别慢的线程给找出来,添加 -XX:+SafepointTimeout 和 -XX:SafepointTimeoutDelay=2000 两个参数,让虚拟机在等到线程进入安全点的时间超过 2000 毫秒时就认定为超时,这样就会输出导致问题的线程名称,得到的日志如下所示:

图片

从错误日志中顺利得到了导致问题的线程名称为“RpcServer.listener,port=24600”。

我们已经知道安全点是以“是否具有让程序长时间执行的特征”为原则进行选定的。

所以方法调用、循环跳转、异常跳转这些位置都可能会设置有安全点,但是HotSpot虚拟机为了避免安全点过多带来过重的负担,对循环还有一项优化措施,认为循环次数较少的话,执行时间应该也不会太长,所以使用int类型或范围更小的数据类型作为索引值的循环默认是不会被放置安全点的。

这种循环被称为可数循环(Counted Loop)。

相对应地,使用 long 或者范围更大的数据类型作为索引值的循环就被称为不可数循环(Uncounted Loop),将会被放置安全点。

通常情况下这个优化措施是可行的,但循环执行的时间不单单是由其次数决定,如果循环体单次执行就特别慢,那即使是可数循环也可能会耗费很多的时间。

HotSpot原本提供了-XX:+UseCountedLoopSafepoints 参数去强制在可数循环中也放置安全点,不过这个参数在 JDK 8 下有 Bug,有导致虚拟机崩溃的风险,所以就不得不找到 RpcServer 线程里面的缓慢代码来进行修改。

最终查明导致这个问题是 HBase 中一个连接超时清理的函数,由于集群会有多个 MapReduce 或 Spark 任务进行访问,而每个任务又会同时起多个 Mapper/Reducer/Executer,其每一个都会作为一个 HBase 的客户端,这就导致了同时连接的数量会非常多。

更为关键的是,清理连接的索引值就是 int 类型,所以这是一个可数循环,HotSpot不会在循环中插入安全点。

当垃圾收集发生时,如果 RpcServer 的 Listener 线程刚好执行到该函数里的可数循环时,则必须等待循环全部跑完才能进入安全点,此时其他线程也必须一起等着,所以从现象上看就是长时间的停顿。

找到了问题,解决起来就非常简单了,把循环索引的数据类型从 int 改为 long 即可。

但如果不具备安全点和垃圾收集的知识,这种问题是很难处理的。

后话

怎么样,看完之后是不是感觉有点意思啊?

至少对于安全点、安全区域这些概念有了稍微深入的理解吧?

然后我问你几个问题:

原作者的原文里面的代码是截图,我专门打出来给你文本。那么你有没有粘出来跑一下?

我跑了。

有没有按照文章对应的改一下代码?

我改了。

有没有把前面说的 JVM 参数都设置进去,对比一下设置前后的差异?

我设置了。

文章中提到的几个链接,你有没有想着找个时间打开看看?

我都看了。

stackoverflow 上那个回答问题的哥们说“在 JDK10 和更高版本中没有这个问题了”,你验证了没?

我用 JDK 15 验证了。

图片

......

图片

哦,对了,你知道为什么我一看到原文的案例的时候就想起了第三版书里面的案例吗?

图片

左边是第二版,右边是第三版。

因为这个案例是新加的,于是我当时特别看了一下,又找到原文去瞅了一眼,于是印象有点深刻。

你看,就是这种这么冷门的东西,看起来学了没卵用的东西,有的时候就是能出奇效。

比如,此时此刻的用途,其实就是我拿来装一下逼而已。

图片

所以,人,没事还是得多读书。书上看到的东西,装逼的时候用一下,能起到点睛之笔的作用。

好了,就这样,收工。

java如何防止反编译 于是,我们就可以这样想:JNI可以调用第三方语言的类库,那么,我们可不可以把解密与装载使用第三方语言写(如C++,因为它们生成的库是不好反编译的),这样它可以把解密出来的class内容直接调jvm.dll的加载接口进行初始化成class,再返回给我们的ClassLoader?嗯,这个方法真的很不错的!由其实现方式上可知,其实现原理只是扰乱正常的代码可读性,并不是真正的加密,如果一个人的耐心很好,依然可以理出整个程序在做什么,更何况,一个应用中,其核心代码才是人们想去了解的,所以大大缩小了代码阅读的范围。 阅读详情

相关推荐

UE4项目迁移指南:如何无缝导入外部.uasset文件并保持材质关联

本文详细解析了将外部.uasset文件无缝导入UE4项目的专业方法。针对手动复制文件时常见的材质关联丢失问题,提供了使用引用查看器进行诊断和修复的实用技巧,并推荐使用引擎内置的“迁移”功能确保资源依赖关系的完整性,帮助开发者高效整合项目资源。

t8u9v0的博客 1046

关于Java并发、JVM面试题

线程是CPU资源调度的基本单位,CPU的执行需要线程的状态数据,比如寄存器信息、程序计数器等,这些信息称为上下文信息。程序计数器:存储了指令的内存地址。指令寄存器(寄存器的一种):存储了将要执行的指令(指令来自程序计数器中内存地址指向的值),CPU会对指令进行分析,交由对应的目标(逻辑运算单元或控制单元)去执行;CPU在处理新任务前,将上下文信息存储到系统内核,并加载新任务的上下文到寄存器和程序计数器。产生死锁要满足四个必要条件:请求保持、互斥性、不可剥夺、循环等待。

梁诗傑的博客 1115

学习记录(十一)--Pattern Recognition投稿记录

上一周特别忽然通知统计建模要答辩,确定是否晋级国赛,一直在准备答辩的事情,所以上一周没有总结周汇报,深刻检讨!!!不过结果是好的,我们组第二名晋级哦。这周主要是跟大家分享一下我投稿PR(Pattern Recognition)的一个时间线,以及投稿后的工作。一、PR从投稿到接收的时间线2025.4.3 Major Revision(ddl4.23) 当时正在弄比赛和另一个着急推进的文章2025.5.30 Minor Revision 这次小修基本上只有一些格式和简单一点点问题。

qq_54949944的博客 1921

Java - Thread.sleep(0)的意义是什么?

sleep(0),是关于RocketMQ源码中写的一段代码,说是用于预防GC的。安全点。可数/不可数循环。STW。navtive方法。趁此机会,对这方面做一个复习和查缺补漏。首先在总结下上面的案例,对于测试代码,出现主线程等待子线程的情况和安全点有关。ThreadOne和ThreadTwo。并在可数循环做一个耗时的操作。此时主线程进入休眠状态1秒钟。JVM有个参数,默认是1秒,1秒之后,JVM就会尝试在安全点停止,以便让Java线程进行缓存的清理。但是对于子线程而言,

Zong_0915的博客 2034

基于openEuler配置LAMP,部署WordPress

openEuler 基于OpenEular操作系统部署WordPress,必须包含以下内容: OpenEular的安装与基本配置。 基于SSH远程管理方式开展操作。 部署LAMP环境。 部署WordPress软件。 提供个人网站服务,并通过浏览器测试应用。 简要讲授相关知识与原理。 cd /etc/yum.repos.d 欧拉系统安装后,没有配置yum源,我们需要重新配置。 [root@localhost yum.repos.d]# ls -l 总用量 4 -rw-r--r--

Axiayouqiaomu的博客 6081

Diffusion、VAE与GAN:三大生成模型的原理对比与应用场景解析

本文深入解析了Diffusion、VAE与GAN三大生成模型的核心原理与特性。Diffusion模型通过逆向去噪过程实现高质量图像生成,但计算成本较高;VAE擅长学习概率潜空间,训练稳定但图像偏模糊;GAN通过对抗训练生成锐利图像,但存在模式崩溃风险。文章结合实战场景,为图像生成、修复、超分辨率等任务提供了清晰的模型选择指南。

weixin_29063979的博客 346

JVM safe point 安全点和 counted loop 可数循环

图中主线程在进入 safe point 后,发现 STW 标志位为 true(为什么 STW 标志位为 true 后面再分析),阻塞在安全点,需要等待一次 GC 执行后才能离开安全点。而且由于 counted loop 花费的时间更长了,导致主线程要在安全点等更久,其他线程才能完成 counted loop,进入安全点。Thread.sleep() 方法是一个 native 方法,在从 native 方法返回后,JVM 会在当前线程设置一个安全点,让线程进入这个安全点。

JellyfishMIX 的博客 1780

比Synergy更轻量!Barrier鼠标共享工具实测:5分钟实现Win/Mac/Linux三设备无缝切换

本文实测了开源免费的Barrier鼠标共享工具,详细指导用户如何在5分钟内于Windows、macOS和Linux三台设备间实现一套键鼠的无缝切换。文章对比了其与Synergy的优势,提供了分平台安装、核心服务端-客户端配置、防火墙设置等完整教程,并分享了解决连接问题的关键技巧,是提升多设备办公效率的实用指南。

weixin_29304021的博客 600

什么?工作五年还不了解SafePoint?JVM的“安全点”揭秘

《深入理解JVM的SafePoint机制》摘要:SafePoint是JVM中让线程暂停的特殊检查点,用于垃圾回收等关键操作。文章通过旋转木马比喻形象说明其工作原理,分析触发时机(GC、方法调用、循环回边)和类型(全局/偏向锁撤销)。重点探讨SafePoint导致的性能问题,如计数循环陷阱,并提供优化方案:使用long循环、方法内联和循环展开。最后给出监控参数和调优建议,强调理解SafePoint对性能优化和问题排查的重要性。掌握这一底层机制有助于编写JVM友好的高性能代码。

2501_93207597的博客 1064

在cadence中进行sdf文件的反标及并进行数模混仿

3.在Main-Include Option Settings-Libraries Files/Directories下面加载上面说到的两个.v文件(综合文件和工艺库器件文件),其他默认点击OK;最近在做一个数模混合电路的流片工作,数字部分综合后会生成一个.sdf(标准延时格式)文件,正常来说应该是要在vcs软件上进行电路延时的反标,但由于我的电路需要信号在数字模块和模拟模块中来回循环多次,因此,打算在cadence中完成sdf文件反标,进行数模混合仿真。1.综合生成的电路网表的.v文件;

WSBHX的博客 3280

delphi fmx android 自动更新(一)

所以,如果是http 则,manifest.xml application 下,https无需添加。增加android:usesCleartextTraffic="true",不然无法使用http下载东西。1,REQUEST_INSTALL_PACKAGES 权限。4,WRITE_EXTERNAL_STORAGE 权限。3,READ_EXTERNAL_STORAGE 权限。一般是从http下载安装包 apk。2,INTERNET 权限。sFile为apk包路径。

全粘工程师 550

JVM 常用参数及safepoint

JVM系列-常用参数_weixin_30739595的博客-优快云博客 每日一面 - JVM 何时会 Stop the world - 简书 1.堆内存 堆内存用于存储new对象,垃圾回收器负责堆内存的管理。但Java程序实际占用的空间则由堆内存、栈内存(程序运行栈)、程序计数器、常量区、代码区、本地内存等。 堆内存分为Young和Old,Young分为2个Survivor (From Survivor和To Survivor),1个eden,具体见JVM系列-垃圾回收。 -Xms??[m|

八荒六合唯我独尊 1252

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

没有发生GC也进入了安全点?这段关于安全点的JVM源码有点意思!

没有发生GC也进入了安全点?程序员的执行结果为何不如预期?深入JVM源码分析问题,原来大佬的注释都这么自信!

CoderW 645

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

遗失的JVM堆内存

“HI,你能不能过来帮我看下这个奇怪的现象?”我之所以会写这篇文章是因为我在一个技术支持的案例中遇到了这么一个情况。这个问题是由于不同的JVM工具所检测出来的可用内存的大小不一致所产生的。 简言之,就是有一个工程师在排查某个应用内存使用过多的问题,而他一直“认为”这个程序的堆是2G的。由于某些原因,JVM工具貌似也不太确定这个进程的堆到底有多大。比如说,jconsole认为这个堆的最大可用内存为1...

ScutKevinYzy的博客 164

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【深入浅出JVM】超经典面试题:Java 中 i = i++ 为何值不变?——从 JVM 字节码深度剖析

初学 Java 时,我们都觉得i++不就是让i加 1 吗?那i = i++不就是"先自增,再赋值给自己"吗?结果应该还是自增后的值啊!int i = 0;i = i++;// 竟然输出 0!如果你把i++换成++iint i = 0;i = ++i;// 输出 1就一个++放前面还是后面的区别,底层到底发生了什么?本文带你拆开 JVM 的"工作台",看懂操作数栈和局部变量表是怎么配合的,以及那条神秘的iinc指令到底做了什么手脚。javap是 JDK 自带的反编译工具,可以把.class。

2301_82211705的博客 672

Java:104-JVM优化

我们可以选择在idea中查看对应的class文件: 但是要注意:随着idea或者说jdk版本的不同,可能并不会出现这些东西或者少些或者有些不同,所以这里以上面的说明为主即可(我修改并整理的) 比如我这里就是这样: Java 虚拟机采用基于栈的架构,其指令由操作码和操作数组成,这些 字节码指令 ,就叫作 opcode,其中,getstatic、ldc、invokevirtual、return 等,就是 opcode,可以看到是比较容易理解的,因为根据后面的说明就是一个完整的System.out.print

qq_59609098的博客 1136

Java入门完全指南:从字节码到JVM的跨平台原理与实战

Java作为企业级开发的中坚语言,其运行机制常被初学者视为黑盒。理解Java程序从源码到执行的完整链路,关键在于JVM(Java虚拟机)与字节码规范。javac将.java编译为平台无关的.class字节码,由JVM负责加载、校验、解释或JIT编译执行,从而实现了“一次编写,到处运行”的跨平台特性。同时,JDK与JRE的分工、类加载双亲委派机制、内存区域划分及垃圾回收策略,共同构成了Java稳定高效运行的基础。这一套机制广泛应用于后端服务、Android及大数据生态。本文从编译运行全流程切入,系统梳理JVM

weixin_30984001的博客 204

JVM实战(28)——模拟Metaspace内存溢出

本章,我们将通过示例代码演示Metaspace区域是如何发生内存溢出的,并根据内存快照进行分析。我们回顾下Metaspace区发生内存溢出的一个场景:程序不停的动态生成类,然后不停的加载类到Metaspace区域,而且这些动态生成的类必须得是不能被回收的,一旦Metaspace区满了,就会触发Full GC,而由于Metaspace区中的对象无法被回收,此时就触发了Metaspace内存溢出。本章,我们通过一个程序示例,不断利用CGLIB生成动态代理类的方式,模拟了Metaspace区内存溢出的场景。

smart_an的专栏 2175

Java构造器深入解析:从JVM执行链路到继承陷阱与设计模式

Java程序每日通过new创建大量对象,但对象在JVM中的诞生远不止分配内存这一环。构造器被编译为特殊的init方法,负责在堆内存分配完成后完成字段默认值设定、显式初始化与构造体执行。理解类加载、实例初始化、继承场景下父类与子类的执行顺序,是排查反射创建、序列化重建及多态调用异常的基础。围绕构造器重载、this()与super()的语法约束,还能延伸出不可变对象、单例模式、静态工厂与建造者模式等实战设计。掌握这套机制,不仅能回答为什么子类没写super()也能编译通过,更能在日常开发中避开构造器调用重写方法

weixin_33404102的博客 195

JVM:类加载

加载过程 Loading 1、双亲委派 为什么要做双亲委派:主要是出于安全考虑。举个例子如果没有双亲委派,我们自定义一个类加载器,再定义一个类名字为 java.lang.String,直接把oracle定义的String类覆盖,并把它交给我们自定义的类加载器,让它去加载。我们把自定义的类加载器和java.lang.String打包为一个jar,交给客户。这时,我们就可以动手脚获取用户的敏感信...

屑狐狸 231

双亲委派模型与类加载器实战:从JVM底层机制到线上排查

理解JVM类加载机制是Java工程师进阶的关键一环。类加载器负责将字节码动态装载进运行时数据区,而双亲委派模型则通过父加载器优先的委派流程,确保核心类库的唯一性与安全性。然而在实际工程中,SPI机制、Tomcat容器、热部署插件等场景往往需要打破这一模型,这也引出了线程上下文类加载器、自定义类加载器等一系列技术手段。当线上出现ClassNotFoundException、NoClassDefFoundError或LinkageError时,掌握类加载器的层级关系、命名空间与可见性原则,能帮助开发者快速定位问

weixin_26763955的博客 258

java 100==100_Java:100==100为true,1000==1000也可以为true!

现象我们先运行一段代码,代码片段如下:Integer a = 1000, b = 1000;System.out.println(a == b);//1Integer c = 100, d = 100;System.out.println(c == d);//2复制如果基础比较好的同学,一眼就能看出来,这段代码结果为false和true,即两个值为1000的Integer不想等,但是两个值为100...

weixin_39774044的博客 364
上一篇: 没有二十年功力,写不出这一行代码!
下一篇: 一/亿眼无法丁真,LLM Chat必定会发生世界级安全事故
IT界那些事儿
博客等级 码龄6年 2万+粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值