上回经分析认为是由于没有关闭输入输出流导致的,但关闭IO流两天后,又出现系统内存溢出。看来,没有从本质上解决问题。
是什么原因导致此问题的产生的呢?还是验证在其中捣乱:只有USER模块使用的比较频繁,USER模块中首页的访问量是最大的,并且首页验证码功能需要在IO流中操作。
再分析下代码,看看是不是有问题。发现如果其中有问题,只有是在
// 在内存中创建图象
int width = 88, height = 19;
BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "JPEG", out);时才可能出问题的。但是要怎么验证这个问题? 使用工具TaskInfo。
通过这个工具发现正常情况下会在windows的临时目录中保存验证码图片,当页面显式后马上就删除了,但是当存在用户大量访问页面时,其中保存在临时目录中的图片将不会被删除掉,并且一直有句柄指向,导致没有释放的句柄越来越多,再次生成图片时会出现Too many open files异常,验证码无法正常显示,用户无法登录。
既然write方法不可能将IO流完全关闭,那我们就自己关闭了。
stream = ImageIO.createImageOutputStream(out);
ImageIO.write(image, "JPEG", stream);
并在finally中显式关闭掉stream流和out流。
这样在用TaskInfo观测发现即使有大量用户访问时,在临时目录中生成的临时文件也会马上删除的。
还不能高兴太早...按上面方法解决,大量用户访问时,过两三天定会出现内存溢出现象。 另谋高就,使用第二种工具Optimizeit.
经用Optimizeit工具分析发现,当存在大量并发请求的时候,其中验证码的实现的确有内存溢出的现象,其中有2个对象没有释放:
java.awt.image.DataBufferByte
java.awt.image.DateBufferInt 经发现是ImageWriter.write()在执行时调用
org.apache.coyote.tomcat5.OutputBuffer.doFlush()方法时发生
java.net.SocketException: Connection reset by peer: socket write error异常时没有将这2个对象释放掉。 【最终解决方案】:
由于是ImageWriter.write()这个方案有问题,那么就换种实现方案。既然是向out流中写图片,那么就直接写吧。
ServletOutputStream out = null;
try
{
out = response.getOutputStream();
JPEGImageEncoder encoder =JPEGCodec.createJPEGEncoder(out);
encoder.encode(image);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if ( null != out )
{
try
{
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
再用TaskInfo和Optimizeit工具测试了下,发现没有生成临时图片,也没有发现内存溢出现象。 一波三折,终于解决问题了。 【经验总结、预防措施和规范建议】 1、网上的代码不一定都是正确的
验证码相关代码,网上多的要命。但有些仅仅是提供实现思路,其并没有考虑到性能问题。在使用前,一定要多考虑。 2、善于求助
在解决此问题的时候,及时请教有经验的同事,并且获得了很好的帮助,大大减少了定位问题时时间。 3、对于IO流的关闭一定要显式关闭
在处理问题时发现,需要在finally中显式的关闭流,这样可以确保流会被正确的关闭,以避免在发生异常导致流没有完全的被关闭,养成良好的编程习惯。
解决方案 »
免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货