1. 在IDEA中,调试模式和直接运行模式,执行相同的代码,调试模式执行得慢
我是在执行一个for循环的时候发现的,本来是想统计for循环的执行耗时,结果意外发现只要打了断点(即使在最后一行的sout语句上打断点),执行时长就会增加数倍。比较好奇,以前从来没想过,问了下gpt。以下是可能的原因:
-
断点和调试信息:
-
断点处理:在调试模式下,IDEA会插入断点。这意味着每当程序执行到断点处时,程序会暂停,以便开发者查看程序状态。这种暂停和检查会显著增加程序的执行时间。
-
调试信息的插入:调试模式会插入额外的信息和代码来追踪变量和调用栈。这些插入的代码会增加执行的开销。
-
逐行执行和代码检查:
-
逐行执行:调试模式允许逐行执行代码,以便开发者能够查看每一行代码的执行情况。这种逐行检查会导致程序运行速度显著降低,因为程序不能连续执行,而是每执行一行就需要停下来等待用户的操作。
-
代码检查:调试器会检查和显示当前作用域内的所有变量的值。每当变量发生变化时,调试器都需要更新这些值,这增加了额外的处理时间。
-
禁用优化:
-
禁用JIT优化:在调试模式下,为了确保程序行为与源代码严格一致,JVM可能会禁用某些即时编译器(JIT)优化。这些优化通常在正常运行模式下大大提高代码的执行效率,但在调试模式下被禁用或减少使用,以便调试信息准确无误。
-
内联和循环展开禁用:某些优化技术(如方法内联、循环展开等)在调试模式下可能被禁用。这些优化通常能显著提高程序性能,但在调试模式下,它们可能导致调试信息不准确。
-
额外的系统资源开销:
-
调试器线程:调试器本身需要运行多个线程来监视和控制被调试程序。这些额外的线程和资源消耗会导致程序执行速度变慢。
-
内存和处理器开销:调试器需要额外的内存和处理器资源来保存调试信息、断点、变量状态等。这些额外的资源消耗会影响程序的执行性能。
我觉得最大的点,可能在禁用优化。
2. 什么是:循环展开、循环重排、方法内联
1. 循环展开(Loop Unrolling)
循环展开是一种优化技术,其主要目标是减少循环控制的开销(如循环计数器的增加和终止条件的检查),从而提高循环执行的效率。例子:
原始代码:
for (int i = 0; i < 8; i++) {
array[i] = i * 2;
}
展开后的代码:
array[0] = 0 * 2;
array[1] = 1 * 2;
array[2] = 2 * 2;
array[3] = 3 * 2;
array[4] = 4 * 2;
array[5] = 5 * 2;
array[6] = 6 * 2;
array[7] = 7 * 2;
或者部分展开
for (int i = 0; i < 8; i += 2) {
array[i] = i * 2;
array[i + 1] = (i + 1) * 2;
}
2. 循环重排(Loop Reordering)
循环重排是优化内存访问模式的技术,通过调整循环的顺序,改善数据局部性和缓存性能,提高缓存命中率,从而提高执行效率。例子:
假设数组是按行存储的
原始代码:按照列写数据
for (int j = 0; j < M; j++) {
for (int i = 0; i < N; i++) {
array[i][j] = i + j;
}
}
重排后的代码:按照行写数据,可以更好利用缓存
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
array[i][j] = i + j;
}
}
3. 方法内联(Method Inlining)
方法内联是一种优化技术,它将函数调用替换为函数体的实际代码,从而消除函数调用的开销。例子:
原始代码:
int square(int x) {
return x * x;
}
int result = square(5);
内联后的代码:
int result = 5 * 5;
其他JIT优化技术
除了上述优化技术,JVM的JIT编译器还使用其他多种优化技术,包括:
- 逃逸分析:确定对象是否在方法之外被引用,如果没有,可以在栈上分配对象而不是堆上,从而减少垃圾回收开销。
- 常量传播和折叠:将常量值直接传播并折叠,从而减少计算量。
- 死代码消除:移除不影响程序结果的无用代码,减少执行负担。
- 分支预测:优化分支条件,提高分支预测的命中率,减少分支错预测的开销。
3. org.apache.commons.collections4.MapUtils
这类里面有个getString挺好用的,还支持写defaultValue,以前都是自己写方法,现在可以直接用了。
4. idea,debug,Evaluate
debug的时候,可点击小计算器图标(ctrl+u)弹出Evaluate窗口,在这里我们可以写代码然后执行代码,灰常方便,以前想看点东西,还得复制出来,再到notepad++中查询,现在可是不用啦
5. idea快速判断
判断条件在开发过程中使用频率非常高,如何快速的写出判断条件呢?
boolean.if 可以生成if(boolean)
boolean.else 可以生成if(!boolean)
string.null 可以生成if(string == null)
string.nn 可以生成if(string != null)
string其实没啥用,因为普遍都使用StringUtils
6. 关于日志
以前记录日志是为了开发阶段能找到问题,对于线上出现问题如何通过日志快速排查,没想过。或许以后记录日志要更多地考虑线上,考虑一旦有问题,该怎么记录日志才能更快地解决问题。
7. 多用虚拟化
以后要把项目地组件打成docker镜像,方便随时启动,随时测试。其实不只是组件,项目也可以打出来,随时想看都可以看。再不济用虚拟机,,把组件和项目扔到虚拟机中去跑,不用了挂起,用的时候迅速切换。
8. IDEA中没有SVN选项的解决方法(没有git箭头,没有svn箭头)
- 在顶部菜单栏中选择“VCS”选项。
- 在下拉菜单中选择“Enable Version Control Integration”。
- 在弹出的窗口中,选择“Subversion”作为版本控制系统。
9. 记录一次bug
升级项目,打包发版,在自己电脑上正常运行,于是拷贝给测试,在测试电脑上一直提示密码错误。
第一时间,肯定是以为密码错了,于是在数据库替换密码,发现不对。
第二,认为是数据库连接错了,于是检查配置,不对。
第三,认为打包有问题,重新打包,还是一样,只有测试电脑上不行。
第四,换nacos配置和数据库,把我的给到测试,还是不行。
最终,精彩的来了。我把测试的redis端口,从127.0.0.1改为他的局域网ip,重启gateway服务,竟然可以登录进去了!检查redis配置,发现写的确实是127.0.0.1,有那么一瞬间大脑都要萎缩了。
明显不符合逻辑,于是进行了更细致的检查,检查代码发现gateway中用到了redis,登录之前去查询了一下redis。摸不着头脑,为何写局域网IP可以呢?这岂不是查不出来吗?
对!就是查不出来就可以正常登录!所以测试那里一定是存在这个参数!而且redis重启后也存在,所以此参数是持久化了。经过检查,确实存在此参数,删除即正常,至此问题解决。
那为何会有此参数呢?经过排查发现,是我第一次打包误操作,开错了加密方式。真是搬起石头砸自己的脚。排查问题的能力还是有待提高,这次虽然比以往快一点,但是还没有到最优化,依然可以有很多优化的地方,比如替换环境,替换的不够彻底,要不早成功 了。何必打那么几次包呢?
10. spring中读取nacos配置文件给工具类的静态变量赋值
@Component
piblic class test {
public static String A;
@Value("${nacos中配置的key}}")
public String a;
@PostConstruct
public void init() {
A = a;
}
}
11. @PostConstruct
它用于在依赖注入完成后,需要执行初始化操作的方法上。具体来说@PostConstruct注解的方法会在依赖注入完成后自动调用,用于执行任何初始化工作。
上面的例子就是先实例化test,注入a的值,然后调用init方法。
具体执行顺序
- 实例化:Spring容器实例化bean。
- 依赖注入:Spring容器将所有依赖注入到bean中。
- @PostConstruct:调用标记了@PostConstruct的方法。
注意事项:
- 每个bean只能有一个标记为@PostConstruct的方法。
- @PostConstruct方法不能有任何参数,并且返回类型应该是void。
- 如果类中没有使用Spring注解或者没有被Spring容器管理,则@PostConstruct方法不会被调用。
12. @DependsOn注解
在Spring框架中,@DependsOn注解用于定义Bean之间的依赖关系。它指定一个或多个Bean,这些Bean必须在当前Bean被初始化之前初始化。这个注解主要用于解决Bean初始化的顺序问题,以确保某些Bean在其他Bean之前被创建和初始化。
@DependsOn可以在类级别或方法级别使用。它接受一个或多个Bean名称作为参数,这些Bean名称必须在当前Bean之前初始化。
- 慎用@DependsOn: 使用@DependsOn可能导致代码变得复杂,建议仅在必要时使用。
- Bean名称: 确保在@DependsOn中指定的Bean名称是正确的,并且这些Bean确实存在。
- 依赖环路: 避免创建依赖环路,否则会导致Spring容器初始化失败。
13. @Order
@Order 注解在 Spring 中是控制 bean 初始化顺序的一种便捷方式。通过合理使用 @Order 注解,可以确保在 Spring 容器初始化时,各个 bean 的顺序满足业务逻辑或者依赖关系的要求。
@Order 注解可以用在类级别(标记在类名上)或者方法级别(标记在初始化方法上)。
数值越小,优先级越高,默认的顺序是 Ordered.LOWEST_PRECEDENCE。
14. CommandLineRunner
CommandLineRunner是Spring Boot提供的一个接口,用于在Spring应用程序启动完成后执行一些特定的代码。实现CommandLineRunner接口的bean会在所有的Spring Beans都初始化完成之后自动执行run方法。它通常用于在应用程序启动时执行一些初始化操作、检查或运行脚本。
注意:是spring启动完成之后执行!