背景
在日常开发中,自己或者同事也遇到过不少系统异常,是很宝贵的经验,以后会统一记录维护下来
JVM系列
动态代理产生过多的类,而没有回收
现象
外部请求几乎全是超时,jvm一直进行老年代回收,却无法回收,系统不可用
原因分析
解决办法
升级jar包版本,回收动态代理类
延伸阅读
深入理解java虚拟机第二版
full gc后,仍有较大内存没有被回收
现象
老年代频繁full gc,每5小时一次,而且每次回收后总有1g左右的内存无法回收,
配置:-Xms2048m -Xmx4096m -XX:NewSize=512m -XX:MaxNewSize=512m
原因分析
当然,在发现问题就找运维要了dump,发现大量ThreadLocal中存有15M左右的日志数据,经过排查代码,发现是公司的日志
框架中,使用了ThreadLocal保存日志,使用完之后却没有remove,而是通过StringBuffer.setLength(0)来重置指针,
导致StringBuilder中字符数组的内存没有被清除,而由于使用线程池,所以内存一直无法释放。
解决办法
ThreadLocal.remove
延伸阅读
承接上面一个问题,修改之后full gc还是比较频繁
现象
full gc 由原来的5个小时/次,变成了15小时每次
原因分析
正常情况,老年代内存的产生有几种情况:
- 新生代GC后,超过eden存储大小的对象
- 大对象直接进入老年代
- 年龄到了设置值的对象
- 动态年龄判断,提前晋升
在请求量没有太大变化的情况下,一般都是大对象,大接口的问题,导致较多对象一直无法释放,或者直接进入老年代。
上面还有一个现象是,跟其他服务对比,该服务年轻带gc很不频繁,感觉就像是上面说的第二点,大对象直接进入
老年代了,所以同事让运维申请了内存,整体扩充一倍,老年代gc给缓下来了,在不改代码的情况下,似乎也没什么
特别好的方式了。。当然这里也问过为什么不是只升级新生代,连坐老年代感觉收益不大,没有得到很好的答复,
后面再问下。
解决办法
升级内存,当然这方法指标不治本,根本方法是修改代码,快速释放内存,修改调用方式,将查询全部的接口改为分页查询等
延伸阅读
深入理解java虚拟机第二版
事务系列
使用spring事务隔离级别死锁
现象
大量报错,提示获取不到数据库链接
原因分析
两个事务,外部事务隔离级别为REQUIRED,内部事务REQUIRES_NEW,由于内部和外部,都更新同一条数据,导致死锁
解决办法
都用REQUIRED
其他
iterator.hasNext 判断有值就continue
现象
集群中cpu使用率百分之一千,因为得不到时间,所以请求时间大量超时,线程得不到释放,所以又不听加线程
原因分析
这个是同事发现的问题,知道的时候已经有运维发现的线程dump了,线程状态RUNNABLE,运行到continue的位置,
那基本已经定位了。
解决办法
continue之前,先调用iterator.next();
延伸阅读
其实网上关于如何定位cpu占用率过高的帖子比较多,一年前遇到这个问题的时候还不知道怎么解决好,
CPU占用过高分析