news 2026/7/7 19:27:01

优化程序性能:JVM 会对final变量进行优化(如编译期常量折叠,直接将常量值嵌入字节码中,避免运行时获取);具体含义

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
优化程序性能:JVM 会对final变量进行优化(如编译期常量折叠,直接将常量值嵌入字节码中,避免运行时获取);具体含义

你想深入理解 JVM 对final变量的性能优化 ——** 编译期常量折叠(Constant Folding)** 的具体含义,我会从「核心定义、前提条件、执行过程、代码验证、例外情况」五个方面展开,用通俗的语言 + 直观案例帮你彻底搞懂。

一、常量折叠的核心定义

** 常量折叠(Constant Folding)是 JVM 在编译阶段(由 javac 编译器 + JIT 编译器协同完成)** 对符合条件的final变量进行的优化手段:它会将代码中对final常量的 “引用访问”,直接替换为 “常量的实际值”,并将该值嵌入到生成的字节码中,从而避免程序运行时通过类 / 对象查找获取变量值的开销,提升执行效率。

简单说:就是 “编译时把变量名换成具体值”,运行时不用再去读取变量的内存地址,直接使用现成的值。

二、常量折叠的前提条件(并非所有 final 变量都能被优化)

只有满足以下所有条件的final变量,才会触发常量折叠优化:

  1. 修饰符要求:必须是static final组合(即静态常量,类级别的常量),单独的final(实例变量 / 局部变量)或单独的static(普通静态变量)都不满足核心条件;
  2. 赋值时机要求:必须在「声明时直接赋值」(不能在静态代码块、构造方法等后续逻辑中赋值);
  3. 赋值内容要求:赋值的是「编译期可知的字面量」(即编译阶段就能确定具体值的类型),包括:
    • 基本数据类型字面量(如int a = 10boolean b = true);
    • 字符串字面量(如String s = "Java",基于字符串常量池机制);
    • 编译期可计算的常量表达式(如final static int C = 5 + 3,编译器会直接计算为 8)。

不满足以上条件的final变量(如实例final变量、静态final但动态赋值的变量),无法触发常量折叠。

三、常量折叠的具体执行过程(对比普通变量)

我们通过 “普通静态变量” 和 “final static常量” 的对比,看常量折叠的执行流程差异:

1. 普通静态变量(无常量折叠)

java

运行

public class NormalVariableDemo { // 普通静态变量:无final修饰 public static int NORMAL_NUM = 100; public static void main(String[] args) { int result = NORMAL_NUM * 2; // 引用普通静态变量 System.out.println(result); } }

执行流程:

  • 编译期:javac 编译时,仅记录NORMAL_NUM的变量声明,main方法中仍保留 “引用NORMAL_NUM” 的逻辑,字节码中存储的是 “获取NORMAL_NUM的值再乘以 2”;
  • 运行时
    1. JVM 加载NormalVariableDemo类,将NORMAL_NUM初始化到方法区的静态变量区;
    2. 执行NORMAL_NUM * 2时,JVM 需要先去方法区查找NORMAL_NUM的内存地址,读取其值(100);
    3. 再执行乘法运算,得到结果 200。
2.final static常量(触发常量折叠)

java

运行

public class FinalConstantDemo { // final static常量:满足常量折叠所有条件 public static final int FINAL_NUM = 100; public static void main(String[] args) { int result = FINAL_NUM * 2; // 引用final静态常量 System.out.println(result); } }

执行流程:

  • 编译期
    1. javac 编译器识别到FINAL_NUMstatic final且为编译期字面量,触发常量折叠;
    2. main方法中的FINAL_NUM * 2直接替换为100 * 2,并进一步计算为200
    3. 生成的字节码中,不再保留对FINAL_NUM的引用,而是直接嵌入字面量200
  • 运行时
    1. JVM 加载FinalConstantDemo类时,仍会将FINAL_NUM初始化到方法区(供反射等特殊场景使用);
    2. 执行result = FINAL_NUM * 2时,直接从字节码中读取现成的200,无需去方法区查找FINAL_NUM的值,直接赋值给result
    3. 输出结果 200,减少了 “查找变量地址→读取变量值” 的步骤。

四、代码验证:直观看到常量折叠的效果

我们可以通过反编译字节码,验证常量折叠的存在:

步骤 1:编译两个类

将上面的NormalVariableDemoFinalConstantDemo编译为.class 文件(执行javac 类名.java)。

步骤 2:反编译查看字节码

使用javap -v 类名.class命令反编译,查看main方法的字节码指令:

(1)普通静态变量NormalVariableDemomain方法字节码(关键部分)

plaintext

// 读取普通静态变量NORMAL_NUM的值(从方法区获取) getstatic #2 // Field NORMAL_NUM:I // 乘以2 iconst_2 imul // 将结果赋值给局部变量result istore_1 // 输出结果 getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; iload_1 invokevirtual #4 // Method java/io/PrintStream.println:(I)V

可见:字节码中存在getstatic指令,用于运行时获取静态变量的值,没有常量折叠。

(2)final static常量FinalConstantDemomain方法字节码(关键部分)

plaintext

// 直接将常量200(100*2的结果)压入操作数栈,无getstatic指令 ldc #2 // int 200 // 将200赋值给局部变量result istore_1 // 输出结果 getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; iload_1 invokevirtual #4 // Method java/io/PrintStream.println:(I)V

可见:字节码中直接嵌入了200,没有getstatic指令(无需运行时查找变量),这就是常量折叠的直观效果。

五、例外情况:这些 final 变量无法触发常量折叠

以下场景的final变量,JVM 无法进行常量折叠优化,仍会在运行时获取值:

  1. final实例变量:如private final int num = 100;(属于对象,编译期无法确定每个对象的该变量值,即使声明时赋值,也不会常量折叠);
  2. static final但动态赋值

    java

    运行

    // 静态代码块中赋值,编译期无法确定值 public static final int DYNAMIC_NUM; static { DYNAMIC_NUM = new Random().nextInt(100); } // 赋值为方法返回值,编译期无法确定值 public static final String DYNAMIC_STR = getStr(); private static String getStr() { return "Java"; }
  3. final局部变量(非静态):虽然final局部变量不可修改,但它是方法级别的变量,JVM 对其优化有限,一般不触发常量折叠(除非是编译期字面量,但优化效果远不如static final);
  4. final修饰的引用类型(非字符串字面量):如final static List<String> LIST = Arrays.asList("a", "b");,引用地址不可变,但对象内容可变,且引用值(内存地址)编译期无法确定,无法常量折叠。

六、常量折叠的性能优化本质

常量折叠的性能提升,本质是减少了运行时的 “内存访问开销”

  • 普通静态变量:运行时需要从「方法区的静态变量区」读取值,涉及内存寻址和数据读取
  • final static常量:编译期已将值嵌入字节码,运行时直接使用栈上的字面量,无需访问方法区,节省了寻址和读取时间。

这种优化在 “频繁使用常量” 的场景下(如循环判断、数学计算),效果会更明显;对于偶尔使用的常量,优化效果虽不显著,但属于 JVM 的 “无成本优化”,无需开发者额外操作。

总结

  1. 常量折叠是 JVM 编译阶段的优化,将符合条件的final static常量引用,替换为实际值并嵌入字节码;
  2. 触发条件:static final+ 声明时直接赋值 + 赋值为编译期字面量;
  3. 核心优势:避免运行时查找变量的内存开销,提升程序执行效率;
  4. 关键差异:普通变量需运行时getstatic获取值,折叠后的常量直接使用字面量;
  5. 例外场景:实例final变量、动态赋值的static final变量等,无法触发常量折叠。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 4:13:55

团队项目管理软件哪个好?9款协作高效、界面友好的工具对比测评

本文旨在为企业管理系统选型者提供一份基于数据实测与逻辑推演的团队项目管理软件评估指南。团队项目管理软件作为企业数字化转型的核心基础设施&#xff0c;其本质是通过算法与流程规范来解决组织内部的信息不对称与资源冲突问题。 随着企业对数据安全主权意识的增强以及远程/…

作者头像 李华
网站建设 2026/7/7 15:20:42

ADTS (Audio Data Transport Stream)

ADTS (Audio Data Transport Stream) 是AAC音频编码的一种传输流封装格式&#xff0c;专为网络流媒体传输设计。核心特点&#xff1a;每帧独立解码&#xff1a;每个AAC帧前都添加ADTS头信息&#xff0c;允许在任意位置开始解码流式传输友好&#xff1a;支持实时播放&#xff0c…

作者头像 李华
网站建设 2026/7/7 16:04:22

个人财务管理工具 HomeBank v5.9.6 中文便携版

链接&#xff1a; https://pan.quark.cn/s/87fccd2f8c56 介绍 HomeBank 是一款开源免费财务管理软件&#xff0c;可帮助您管理个人账户和开支。HomeBank 易于使用图形和自定义过滤器&#xff0c;旨在为您提供财务概览。如果您正在寻找一个完全免费&#xff0c;简单的应用程序…

作者头像 李华
网站建设 2026/7/6 17:24:07

Excel高性能异步导出完整方案!

大型电商系统中&#xff0c;数据导出是一个高频且重要的功能需求。传统的同步导出方式在面对大数据量时往往会导致请求超时、内存溢出等问题&#xff0c;严重影响用户体验。苏三商城项目创新性地设计并实现了一套完整的Excel异步导出机制&#xff0c;通过注解驱动、任务队列、定…

作者头像 李华
网站建设 2026/7/7 15:04:32

家长必看,别等孩子近视了,再看到这篇文章!

近年来&#xff0c;我国儿童青少年近视问题日益严峻。数据显示&#xff0c;我国儿童青少年总体近视率超过50%&#xff0c;其中小学生超过三分之一&#xff0c;初中生超过70%&#xff0c;高中生甚至高达80%以上。更令人心惊的是&#xff0c;近视危机正呈现低龄化趋势&#xff0c…

作者头像 李华
网站建设 2026/7/7 14:26:25

【计算机毕业设计案例】基于Spring Boot+Vue人力资源管理系统的设计与实现基于springboot的人力资源管理系统的设计与实现(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华