黑马程序员技术交流社区

标题: 异常处理带来的副作用 [打印本页]

作者: 夕阳游子    时间: 2015-9-8 21:31
标题: 异常处理带来的副作用

这问题, 仅仅是特定业务场景下的。 希望别纠缠。
今天看到, 某个string转integer的代码性能非常有问题。 这个代码被极大数量的使用。 代码如下:
[pre]    public Object convert(Object source) {        if (source == null) {            return new Integer(0);        }        String strValue = (String) source;        try {            return new Integer(strValue);        } catch (Exception e) {            try {                return new Integer(strValue.trim());            } catch (Exception ex) {                return new Integer(0);            }        }    }[/pre]刚看上去, 我觉得比较正常, 因为我知道业务数据99%的情况下, 不会有非法的情况。 我测试下1000W次转换的性能:
conv.convert("1234")    550ms
conv.convert(" 1234 ")  26846ms
conv.convert(" ")  51013ms
居然相差了快50
倍的性能。 这个代码仔细看了下, 如果source=" "; source=" 1234 "
这样的情况下, 性能变的特别恶劣, 特别是"", " "
的情况下, 几乎是100
倍的性能差异。 这让我意识到, Java形成异常的过程是非常消耗cpu/mem
的。 我改写了这个代码,做了一下几点改进:
1. "", " " 处理过程中不再产生异常。
2. 测试了下String.trim()性能, 可以直接使用。
3. 居然 new Integer(String s)比 Integer.valueOf(String s) 性能高一点点
[pre]    static Integer ZERO_INTEGER = Integer.valueOf(0);    public Object convert(Object source) {        String strValue = (String) source;        if (strValue == null) {            return ZERO_INTEGER;        }        strValue = strValue.trim();        if (strValue.length() == 0) {            return ZERO_INTEGER;        }        Integer ret = null;        try {            ret = new Integer(strValue);        } catch (NumberFormatException e) {            ret = ZERO_INTEGER;        }        return ret;    }[/pre]我测试下了1000W次性能:
conv.convert("1234")    660ms
conv.convert(" 1234 ")  860 ms
conv.convert(" ")  260ms
虽然, 我们调整了代码, 但是, 正常情况代码的性能是下降了。 这是没有任何办法了。 当然, 为了避免正常情况下trim()的操作可以加上,这要根据生产具体情况了:
[pre]  if (strValue.charAt(0) == ' ' || strValue.charAt(strValue.length() - 1) == ' '){            strValue = strValue.trim();  }[/pre]事情应该到此了, 但是,我还是很怀疑, 这个变化怎么能在生产环境中, 产生如此大的影响,以现在CPU这点性能变化,应该不至于非常致命。应该不同的栈深度的, 造成了更大的性能障碍。我测试了下:
[pre]public Object convert2(Object source) {        if (source == null) {            return new Integer(0);        }        String strValue = (String) source;        try {            return new Integer(strValue);        } catch (Exception e) {            try {                return new Integer(strValue.trim());            } catch (Exception ex) {                return new Integer(0);            }        }    }//使用递归构造栈深度public void rescue(String source, int depth) {                if (depth > 0) {            rescue(source, --depth);        } else {            convert2(source);        }    }[/pre]100万次测试的结果如下:
conv.rescue(" 1234", 0)   2779ms
conv.rescue(" 1234", 1)   3001ms
conv.rescue(" 1234", 5)   3258ms
conv.rescue(" 1234", 10) 3751ms
conv.rescue(" 1234", 50); 7611ms
很明显栈的程度,对于异常的构造, 还是需要不少时间的。
现在我们可以获得几个结论:
1. 在一个框架级应用上, 尽量检查数据后, 再进行类型转换, 尽量避免使用"无谓"的异常, 否则,在高度覆盖的代码上, 可能造成严重的性能问题。特别使用特定的异常进行条件跳转。
2. 尽量少在系统底层抛出异常。 否则在异常栈的构造随着深度, 消耗的时间增加。
这个问题,造成生产系统速度下降了100%+。 别怀疑, 这是非常特殊的应用。 大量的解析xml数据, 然后形成JavaBean对象。 大约有60%的类型是string=>integer转化。 另外声讨下xml, 下一节, 我们会讲述一个恐怖的xml性能故障点, 大部分解析器都存在的问题。
PS: 我刚想了下, 好像我这么测试栈深度是不对的。 会不会是这个条件引起的。 谁有办法测试?
[pre] if (depth > 0) {            rescue(source, --depth);        }[/pre]
[/td][/tr][/table]




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2