公司监控server release一个版本跑了几天,都很正常,优化后尤其兴奋的是
95 小时无FullGC
jstat -gc 39148
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
2048.0 2048.0 1536.1 0.0 694784.0 302150.5 1398272.0 338841.9 25472.0 24952.5 2944.0 2787.9 1856 19.141 0 0.000 19.141
但每次YoungGC后 Old Gen都有一点增长,有一个累积效果,怀疑有Leak
用JProfile本地追一下,发现char[] 和String的 retain size 较大
最后追踪到 业务逻辑拼接生成的大量sql身上
然后发现了如下代码:
List<SensorBean> ls = mdb.getSensorLs();
String e01_value = "NULL,";
...
for (SensorBean sbean : ls) {
if ("E01".equals(sbean.getSensorNo())) {
e01_level = "'" + sbean.getAlarmLevel() + "', ";// getAlarmLevel()等返回都是String
e01_value = "'" + sbean.getDataValue() + "', ";
} else if ("E02".equals(sbean.getSensorNo())) {
e02_level = "'" + sbean.getAlarmLevel() + "', ";
e02_value = "'" + sbean.getDataValue() + "', ";
} else if ("E03".equals(sbean.getSensorNo())) {
e03_level = "'" + sbean.getAlarmLevel() + "', ";
e03_value = "'" + sbean.getDataValue() + "', ";
} else if ("E11".equals(sbean.getSensorNo())) {
e11_level = "'" + sbean.getAlarmLevel() + "', ";
e11_value = "'" + sbean.getDataValue() + "', ";
} else if ("T01".equals(sbean.getSensorNo())) {
t01_level = "'" + sbean.getAlarmLevel() + "', ";
t01_value = "'" + sbean.getDataValue() + "', ";
} else if ("T02".equals(sbean.getSensorNo())) {
t02_level = "'" + sbean.getAlarmLevel() + "', ";
t02_value = "'" + sbean.getDataValue() + "', ";
} else if ("T03".equals(sbean.getSensorNo())) {
t03_level = "'" + sbean.getAlarmLevel() + "', ";
t03_value = "'" + sbean.getDataValue() + "', ";
} else if ("T11".equals(sbean.getSensorNo())) {
t11_level = "'" + sbean.getAlarmLevel() + "', ";
t11_value = "'" + sbean.getDataValue() + "', ";
} else if ("W01".equals(sbean.getSensorNo())) {
w01_level = "'" + sbean.getAlarmLevel() + "', ";
w01_value = "'" + sbean.getDataValue() + "', ";
} else if ("W02".equals(sbean.getSensorNo())) {
w02_level = "'" + sbean.getAlarmLevel() + "', ";
w02_value = "'" + sbean.getDataValue() + "', ";
} else if ("E12".equals(sbean.getSensorNo())) {
e12_value = "'" + sbean.getDataValue() + "', ";
}
}
之前看到加号连接我都会告知用StringBuffer,避免产生临时字符串,但没有给出具体依据,不能完全让人信服,
特别是jdk5 以后 +会被编译器自动优化成 StringBuilder,所以有必要深究一下
抛开其他逻辑不管,只针对这段代码的String 加号连接部分,问这样几个问题:
1, 这里面加号会被转化成什么形式?
2,放在循环里 加号连接为什么不好?
所以单独写个小例子,然后javap 查看字节码
左侧代码,右侧是对应的字节码
第一个在循环内new StringBuilder
justAdd.jpg
第二个仅仅是StringBuffer.append
stringAppend.jpg
总结
- 如果只是局部变量,+加号连接可以接受,会被编译器优化成StringBuilder 局部非线程安全
- 如果是循环中则要用循环外定义好的StringBuffer,然后逐一append
- 如果循环中 用String + 的后果是什么?
每加一个字符串都会生成一个新字符串,然后丢弃原有,
生成大量临时字符串,给GC造成压力
参考
Java8字符串连接:
http://www.pellegrino.link/2015/08/22/string-concatenation-with-java-8.html
Oracle 官方 String 问答, benchMark评测(大致浏览下,没细看...)
https://shipilev.net/talks/joker-Oct2014-string-catechism.pdf
网友评论