最近公司有一项业务在做活动,流量一下子大增。数据暴涨了80,系统无法支撑,导致了程序内存溢出,系统宕机。查看日志发现是有内存溢出的异常,今天就为大家分享一下如何使用 jstack 命令排查定位java程序中的异常代码。
自造 java 内存溢出代码
为了讲清楚 jstack 的使用,我们先自造一个java 内存溢出的程序。程序代码如下:
class KeylessEntry { static class Key { Integer id; Key(Integer id) { this.id = id; } @Override public int hashCode() { return id.hashCode(); } } public static void main(String[] args) { Map m = new HashMap(); while (true) for (int i = 0; i < 10000; i++) if (!m.containsKey(i)) m.put(new Key(i), "Number:" + i); } }
当你运行上面的代码时,你可能会期望它运行起来永远不会出问题,毕竟内置的缓存方案只会增加到10,000个元素,然后就不会再增加了,所有的key都已经出现在 HashMap中。然而,事情并非如此。元素将会一直增长, 因为Key这个类没有在hashCode()后实现一个合适的equals()方法。
运行这个程序后,我们发现不需要太长的实际,程序就报内存溢出了。打开任务管理,此例中,找出java进程ID。
会了方便,大家也可以使用使用ProcessExplorer找到ID号为7064的java进程。进程ID为7064的属性信息在Thread标签找到CPU利用率的线程信息,TID为6120(10进制)
将CPU利用率高的线程ID 6120(10进制)转换为0x17E8(16进制)
使用jstack查看进程7064的线程信息。找到线程号为0x17E8的线程。命令:
jstack -l 7064
根据提示的行号,我们定位到相关的代码。通过分析发现,是map造成的内存溢出。
解决方法很简单,只要和下面的示例一样添加一个equals方法就可以了。但是在找到问题所在之前,你肯定已经花费了不少宝贵的脑细胞。
@Override public boolean equals(Object o) { boolean response = false; if (o instanceof Key) { response = (((Key)o).id).equals(this.id); } return response; }
jstack 命令很实用,是java虚拟机自带的一种堆栈跟踪工具。学会它对你的职业生涯有很大的帮助!
: » 详解使用 jstack 跟踪 java 异常代码
原创文章,作者:6024010,如若转载,请注明出处:https://blog.ytso.com/tech/java/251537.html