栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

程序bug排查心得

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

程序bug排查心得

程序bug排查心得
  • 1.Bug排查基本流程
  • 2.常用的一些bug排查方法
    • 2.1 搜索引擎
    • 2.2 print or logger大法
    • 2.3 debug和工具的使用
    • 2.4 二分法
  • 3.记一次bug排查经历
    • 3.1 注意java集合的使用问题
    • 3.2 注意触发类加载的时机
  • 4.总结

代码出现bug对于程序员来讲再正常不过了,即使再资深的程序员也难逃厄运。那么如何能够快速的排查和解决bug,将成为程序员的基本功。

1.Bug排查基本流程

对于程序中出现的bug,我们可以按以流程进行排查:

2.常用的一些bug排查方法 2.1 搜索引擎
  • 在我们所遇到的绝大部分bug已经被别人修复并且分享出来了,尤其对于不熟悉的bug或者报错,可以通过Google/Stack Overflow/Github等工具进行查找,能够解决一些基本的bug。
2.2 print or logger大法
  • 对于一些不具备debug环境,或者认为debug不够高效,可以采取print 或 logger方式打印对应位置的日志信息,根据日志信息进行分析定位bug。
  • 如我们项目所依赖各种环境,无法在本地启动进行调试,可以采取该方法;
  • 对于多线程调试,debug方式不是很方便,往往也采取该方法。
2.3 debug和工具的使用

一些常用的debug工具:

  • chrome develop tools 通常用于对前端问题进行调试;
  • IDE debugging 支持本地debug 、 远程debug 、 也可进行多线程调试;
  • wireShark(抓包分析工具,能够帮助你快速定位到网络相关的问题);

关于远程调试工具配置可参考这篇博文:
https://blog.csdn.net/qq_37192800/article/details/80761643

2.4 二分法

大致的流程就是,把代码注释掉一半,保留另一半,运行,看哪一半会导致问题,然后把那一半再分半,再定位到1/4,接下来以此类推,1/8,1/16 一会就到大致的位置了,再分析下那个位置,如下图所示:

3.记一次bug排查经历

在最近项目中,遇到了比较棘手的bug问题,大体情况如下:
需新上线一个功能,在本地单元测试是正常运行,上线后出现了一个无法稳定复现的bug, 且在该bug里面还存在一个其他bug,由于两个bug揉到一起,排查定位难度较高,一度让人崩溃,后续冷静下来,分析定位以及请教同事最终解决,这里记录下所遇到的两个bug:

3.1 注意java集合的使用问题
import java.util.HashMap;
import java.util.Map;
import java.util.Set;


public class TestSetOpera {

    public static void main(String[] args) {
        Map hashMap1 = new HashMap<>();
        hashMap1.put(22L, "test22");
        hashMap1.put(33L, "test33");
        Set longSet = hashMap1.keySet();
        Map hashMap2 = new HashMap<>();
        hashMap2.put(33L, "test33");
        hashMap2.put(44L, "test44");
        longSet.addAll(hashMap2.keySet());
        System.out.println(longSet);
    }
}

运行报错:

代码中,使用集合时,往Set< Long >集合中直增加了Set< Long >,程序运行时,将会调用抽象类的add(),
该方法将直接抛出异常,导致:java.lang.UnsupportedOperationException。
可以将这段代码:

        Set longSet = hashMap1.keySet();

改为:

        Set longSet = new HashSet<>(hashMap1.keySet());

这样,该段代码在运行时,通过多态的方式,会调用具体实现类的addAll()方法。

3.2 注意触发类加载的时机
// 放部分代码,不影响理解
public static HashMap<> CONFIG_MAP;

static {
     ScheduledExecutorService servicePool = Executors.newScheduledThreadPool(1);
     servicePool.scheduleAtFixedRate(SpeechscriptPool::refresh, 0, 10, TimeUnit.SECONDS);

     LOG.info("start background SpeechscriptPool thread successfully.");
 }
 
private static void refresh() {
     try {
         LOG.info("begin to refresh script in memory.");
         Thread.currentThread().setName("refreshscript-thread");
         String updateTime = DateUtils.getNSecTime(script_TTL);
         // 。。。操作集合的bug
         // 读取配置文件更新内容,设置到静态变量中
         CONFIG_MAP = getConfig();
         // 省略后续代码。。。
     }
  }

这段代码中,原意是想通过当加载该类时,启动一个定时器,每隔10s,该定时器会读取配置文件中的最新配置设置到静态变量中去,然后当有请求进来,会直接读取静态变量中的最新值做后续操作。程序在集群中部署后,服务重启后,一些请求进来,有时会获取到值,有时会为null,bug无法稳定复现。

后面经过一番排查,找到导致bug的原因:在服务启动时,jvm并没有加载该类,而是直到有请求进来,需要访问该类的静态方法、变量,此时才触发去加载这个类,然后定时器将配置文件中的值读取到静态变量中。又因为静态变量仅存在于jvm内存的方法区中,在分布式场景中,就会出现当请求访问到还没加载到该类到节点时会报null,访问到已经加载该类到节点则正常。

以下为java中触发类加载的时机图:

后面,通过在服务的启动的初始化方法中,访问该类,让该类在服务启动时便加载进来。

4.总结

对于如何能够尽可能的减少bug总结了一些经验:

  • 尽可能的清楚你要做什么,先不着急写第一行代码,多想想代码应该如何设计和组织;
  • 尽可能的集中精力写代码(日常中,大家的很多时间都会被割裂的四分五裂,尽可能的的把一些有挑战性的、复杂的coding工作放在一段连续时间内,对于提升效率减少bug都大有帮助);
  • 尽可能多的写注释(利己利人);
  • 尽可能的掌握所用到技术的一些基本原理,有助于分析所出现bug;
  • 弱类型语言尽可能的保持类型一致;
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/458585.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号