详解使用 jstack 跟踪 java 异常代码

最近公司有一项业务在做活动,流量一下子大增。数据暴涨了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。

任务管理器中查看java进程id

会了方便,大家也可以使用使用ProcessExplorer找到ID号为7064的java进程。进程ID为7064的属性信息在Thread标签找到CPU利用率的线程信息,TID为6120(10进制)

ProcessExplorer 查看进场的线程信息

将CPU利用率高的线程ID 6120(10进制)转换为0x17E8(16进制)

10进制转16进制

使用jstack查看进程7064的线程信息。找到线程号为0x17E8的线程。命令:

jstack -l  7064

jstack 定位异常代码

根据提示的行号,我们定位到相关的代码。通过分析发现,是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 异常代码

: » 详解使用 jstack 跟踪 java 异常代码

原创文章,作者:6024010,如若转载,请注明出处:https://blog.ytso.com/tech/java/251537.html

(0)
上一篇 2022年5月3日 04:34
下一篇 2022年5月3日 04:39

相关推荐

发表回复

登录后才能评论