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

JAVA herman 1328浏览 0评论
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

最近公司有一项业务在做活动,流量一下子大增。数据暴涨了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虚拟机自带的一种堆栈跟踪工具。学会它对你的职业生涯有很大的帮助!

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » 详解使用 jstack 跟踪 java 异常代码