论坛首页 入门技术论坛

java线程死锁与内存溢出

浏览 36514 次
该帖已经被评为新手帖
作者 正文
   发表时间:2009-01-06   最后修改:2009-01-06

在浏览这个帖子之前请各位耐心的看把这个帖子看完http://topic.csdn.net/u/20080222/21/880253ba-d9a7-4ec8-a8ee-6821eb9563c0.html。我的问题和他的一模一样,java开发的环境也基本一样。

项目描述:河北省95598客服系统,JDK1.5,Tomcat5.5,服务器是2K,物理内存2G,Tomcat内存512M。JProfiler监控内存使用。

异常描述:先是河北省的一个市在用,市局坐席加县局坐席大约50-60个人,也就是说每天肯定不会低于50个人在用。当项目上线5分钟后内存呈60度上升趋势,直到内存溢出,然后又呈90读趋势下降,然后会平稳一段时间。这样反反复复几次以后tomcat就挂掉了!如下图:

 

第一次出现这个问题的时候我把问题归结到是因为项目中使用了大量的ArrayList之后并没有及时clear掉,然后我们从头到尾的把项目中的ArrayList全部clear了一遍,项目重新发布结果---依然溢出。

第二次改动的地方是项目中包,我发现项目中有很多重名的包,但是版本不一样,怀疑是因为包冲突而导致的内存溢出。然后就把所有重名的包给全部删除只留版本较高的包。项目重新发布结果---依然溢出。

第三次改动是发现了DWR的一个 DefaultScriptSessionManager.invalidate): removeCount=0 when invalidating: DefaultScriptSession[id=${scriptSessionId}403]异常,然后去网上查了一下,这是因为DWR的内部处理线程的时候报的异常,在最新版本中已得到解决。看它的描述和我们的差不多,呵呵,我像看到救星一样乐呵呵的把DWR的最新程序包替换掉,心想这次总应该没问题了。项目重新发布结果---涛声依旧。现在身上开始出汗了!

就在我快要绝望的时候无意间发现了这个东西,下图:

 

DDconnectionBroker??这是我用的连接池啊。Blocked??堵塞???难道是。。。。。。。下面是我的连接池的代码

package com.freebridge.dbConnection;


import java.sql.*;

import com.devdaily.opensource.database.DDConnectionBroker;
import comm.freebridge.getProperties.ClassGetProperties;

public class Data {
	private Connection Conn;
	private Statement stmt;
	ClassGetProperties ClassProperties;
	String StrDrive;
	String StrUrl;
	String StrDbUsername;
	String StrDbPassWord;
	int IntMaxConnectionCount;
	int IntMinConnectionCount;
	int IntManageType;
	public static DDConnectionBroker broker = null;
	String type="mysql";
	String type2="mysql";
	private PreparedStatement PrapStmt=null;
	private CallableStatement ProcStmt=null;
	private boolean success = false;

	public void getProperties(){//现改为当broker为null时调用此读配置文件的方法    by张熙
		type="mysql";
		ClassProperties=new ClassGetProperties("dbConnectionConfig.properties");
		StrDrive=ClassProperties.getValue("driver");
		StrUrl=ClassProperties.getValue("url");
		StrDbUsername=ClassProperties.getValue("username");
		StrDbPassWord=ClassProperties.getValue("password");
		IntMaxConnectionCount=Integer.parseInt(ClassProperties.getValue("MaxConnectionCount"));
		IntMinConnectionCount=Integer.parseInt(ClassProperties.getValue("MinConnectionCount"));
		IntManageType=Integer.parseInt(ClassProperties.getValue("ManageType"));
	}
	
	public void  getConnection(){//		创建获取Properties配置文件类 
		
		if (broker == null||(!this.type.equals(this.type2)))
	    {
			this.getProperties();
	
			this.type2=this.type;
		 try{ 
		        broker = new DDConnectionBroker(StrDrive,
		        		StrUrl,
		        		StrDbUsername,
		        		StrDbPassWord,
		        		IntMinConnectionCount,
		        		IntMaxConnectionCount,
		                100,
		                60000,
		                "");
		        }
		        catch (SQLException se)
		       {
		        	//ServerLog.AppendLog(se);	
		        // could not get a broker; not much reason to go on
		        	se.printStackTrace();
		        System.out.println( se.getMessage() );
		        System.out.println( "Could not construct a broker, quitting." );
		        broker = null;
		 
		       }
	    }
		try{
			
			Conn = broker.getConnection();
			stmt=Conn.createStatement();
		}catch(Exception ex){
			ex.printStackTrace();
			//cf.close();
		} 
	}
		/**
	 * 关闭数据库连接
	 */
	public void ConnClose(){
		if (broker != null){
			try{
				
				if(stmt!=null){
					stmt.close();
					stmt=null;
				}
			 broker.freeConnection(Conn);
			}catch(Exception e){
					
				System.out.println(e);

			}
		 }
	}
		/**
	/**
	 * 自动清除数据库链接
	 */
	protected void finalize(){
		ConnClose();
	}

}

 上面代码还是没修改之前的,仔细看一下会发现DDConnectionBroker 是个全局公用的类,在网上查了一下 jsp和servlet都不是单线程执行。也就是说jsp当多人访问时会出现线程并发的情况,也就是说当两个线程同时得到Conncetion时会出现线程死锁,然后由于线程死锁的原因会引起内存的急速上升,直到内存溢出,报错,线程解锁,然后内存又急速下降!

第四次改动是在getConnection和ConnClose前面加上synchronized保证此方法是线程安全的。心里不敢有丝毫的大意,小心翼翼的把项目布置上去。观察,一天,两天,三天。。。。。。直到现在那个魔鬼再也没出现。

我不会去总结什么东西,只是简单的把我的经验和教训写出来,如果你们也遇到和我类似的问题不妨试一下我的办法。

  • 大小: 74.2 KB
  • 大小: 93.7 KB
   发表时间:2009-01-06  
没明白为啥同时获取connection会造成死锁。。
0 请登录后投票
   发表时间:2009-01-06  
  为什么会有2个线程同时获取同一个Connection?
  你是在WEB层获取Connection吗?如果这样,似乎在设计上不可取!
  如果是在应用层(逻辑层),那么每一个请求都是一个独立的线程,不存在同时多个线程同时访问一个Connection的
0 请登录后投票
   发表时间:2009-01-06  
自己写连接池?
0 请登录后投票
   发表时间:2009-01-06  
# try{ 
#              
#             Conn = broker.getConnection(); 
#             stmt=Conn.createStatement(); 
#         }catch(Exception ex){ 
#             ex.printStackTrace(); 
#             //cf.close(); 
#         }  

1.无finally,不关连接。try中的东西出异常你的这个connection费了。

2.如果这个类是单例的,你每次都是在从你的broker中拿连接,但原来你这个单例中如果持有另外的connection,则那个connection在没有关闭的情况下成了无主废人。

3.如果不是单例的,那么broker就被创建多次,我不知道你这个broker是个什么,但如果是连接池,那么每个新的实例会占用你initConnection中设置的数量的连接。
0 请登录后投票
   发表时间:2009-01-06  
repsihWDX 写道
# try{ 
#              
#             Conn = broker.getConnection(); 
#             stmt=Conn.createStatement(); 
#         }catch(Exception ex){ 
#             ex.printStackTrace(); 
#             //cf.close(); 
#         }  

1.无finally,不关连接。try中的东西出异常你的这个connection费了。

2.如果这个类是单例的,你每次都是在从你的broker中拿连接,但原来你这个单例中如果持有另外的connection,则那个connection在没有关闭的情况下成了无主废人。

3.如果不是单例的,那么broker就被创建多次,我不知道你这个broker是个什么,但如果是连接池,那么每个新的实例会占用你initConnection中设置的数量的连接。

broker是个全局的连接池,是在CloseConn中把得到连接放到连接池中,释放掉并不是真正的关闭,这是连接池最基本的概念吧
这个类不是单例但也没有被多次创建,注意这句话 if (broker == null||(!this.type.equals(this.type2)))   ,初始化的时候broker是null而且是全局的,可以保证只被实例化一次。
0 请登录后投票
   发表时间:2009-01-06  
icyiwh 写道
  为什么会有2个线程同时获取同一个Connection?
  你是在WEB层获取Connection吗?如果这样,似乎在设计上不可取!
  如果是在应用层(逻辑层),那么每一个请求都是一个独立的线程,不存在同时多个线程同时访问一个Connection的

这和在什么地方获取连接没关系,tomcat是可以多线程并发访问的,当有两个并发的线程同时在连接池中获取同一个连接的时候就会出现线程死锁,而导致我说的那个结果!
0 请登录后投票
   发表时间:2009-01-06   最后修改:2009-01-06
我指的是你的Data类是不是单例。
如果是单例,那么虽然您的broker是不会重复的,但您每次都从新从broker里面拿连接,但又没传到外面用,赋予了您在单例的data中的那个Connection引用。
那么,如果原来Connection那个引用已经持有了一个连接实例,它在哪儿被关闭呢?

0 请登录后投票
   发表时间:2009-01-06  
repsihWDX 写道
我指的是你的Data类是不是单例。
如果是单例,那么虽然您的broker是不会重复的,但您每次都从新从broker里面拿连接,但又没传到外面用,赋予了您在单例的data中的那个Connection引用。
那么,如果原来Connection那个引用已经持有了一个连接实例,它在哪儿被关闭呢?


是的Data类是个单例,因为在全部的应用程序中只实例化了一次。
呵呵!我发的只是类的部分代码,在全部的代码中已经封装了执行这个conn的程序,也就是说这个Connection是不会被外边得到的,所有的打开和执行和关闭都是在这个封装类完成的!
0 请登录后投票
   发表时间:2009-01-06  
zhangxi123 写道
repsihWDX 写道
我指的是你的Data类是不是单例。
如果是单例,那么虽然您的broker是不会重复的,但您每次都从新从broker里面拿连接,但又没传到外面用,赋予了您在单例的data中的那个Connection引用。
那么,如果原来Connection那个引用已经持有了一个连接实例,它在哪儿被关闭呢?


是的Data类是个单例,因为在全部的应用程序中只实例化了一次。
呵呵!我发的只是类的部分代码,在全部的代码中已经封装了执行这个conn的程序,也就是说这个Connection是不会被外边得到的,所有的打开和执行和关闭都是在这个封装类完成的!

...寒


当我啥都没说好了
0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics