最近在处理jvm内存问题,对jsp数据库操作的内存处理不很清楚,请各位给出意见,最好能有实际的测试依据。一、基本使用
db_test.jsp:
<%
Connection conn = null;
Statement  stmt = null;
ResultSet  rs   = null;
try {
  conn = getConnection(  );  //此处省略获取connection的具体代码
  stmt = conn.createStatement();
  rs   = stmt.executeQuery("select * from tab_a");
  if ( rs.next() ) out.println(rs.getString(1));
}
catch(SQLException ex) {
}
finally{
      if (   rs != null ) {   rs.close();  rs = null;  }
      if ( stmt != null ) { stmt.close(); stmt = null; }
      if ( conn != null ) { conn.close(); conn = null; }
}
%>
想请问:如果以上代码没有问题的运行完毕,是不是rs,stmt,conn所占用的java虚拟机内存都能被回收呢? 
被close()但不是null状态的rs会不会占用内存不能释放呢?
比如finally中只执行rs.close(),stmt.close(),conn.close(),而省略s=null,stmt=null,conn=null,这两种有什么本质的不同呢? 
也就是说rs=null等这些语句有什么明确的作用或者好处呢?
不加上这些赋空语句又有哪些坏处呢?

解决方案 »

  1.   

    二、常见使用
      ......
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      
      rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
      ......
      //执行finally块 
     
      如上一个rs多次利用后最后才进行close()操作,这三次的executeQuery操作中java是怎样使用内存的呢?
      假定第一次query操作需要占用10k内存,第二次query操作需要占用15k内存,那java是在第一次10k基础上再扩展5k内存还是丢弃原来的10K内存而重新申请15k内存呢?如果第三次query只需要2k内存的话,java会将占用的15k自动压缩为2k还是另开辟2k内存呢? 
      最终java会消耗多少内存而最好能回收多少内存呢?   ......
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();    rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();  
      
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();  
      ......
      //执行finally块 
      
      每执行一次Query之后就执行一次rs.close(),与上面的做法有哪些不同呢? 
      java是怎样处理内存的呢?
      

  2.   

    三、javabean + jsp使用模式:
     //javabean
     public class db {
        private Connection conn;
        private Statement  stmt;
        private ResultSet  rs;    public void initdb() {
           ....
        }
        public ResultSet ExecSelect(String sql) {
            stmt = conn.createStatement();
            rs   = stmt.executeQuery(sql);
            return rs;
        }
        public void closedb() {
          if (   rs != null ) {   rs.close();  rs = null;  }
          if ( stmt != null ) { stmt.close(); stmt = null; }
          if ( conn != null ) { conn.close(); conn = null; }
      }
        
        //jsp:A  
        <jsp:useBean id="db" scope="page" class="db"/>    ResultSet rs_jsp = null;    rs_jsp = db.ExecSelect("select * from tab_a");
        rs_jsp.next();
        
        rs_jsp = db.ExecSelect("select * from tab_b");
        rs_jsp.next();    db.closedb();
        如上所示,db.closedb()能否将rs_jsp,db.rs全部close呢? 如果可以close的话,db.rs为null,而rs_jsp应该还不是null吧?
        如果rs_jsp不为null,那么java如何处理内存呢?     //jsp:B (两次select操作按顺序处理的。处理tab_b时,tab_a中的数据已经不需要了)  
        <jsp:useBean id="db" scope="page" class="db"/>
        
        ResultSet rs_jsp = null;    rs_jsp = db.ExecSelect("select * from tab_a");
        rs_jsp.next();
        
        rs_jsp = db.ExecSelect("select * from tab_b");
        rs_jsp.next();    rs_jsp.close();    //这两句话有没有必要加上呢?
        rs_jsp = null;     //这两句话有没有必要加上呢?      db.closedb();
        与jsp:A相比,jsp_B的代码有什么作用呢?//jsp:C(两次select操作是共存处理的。只有等tab_b处理完毕,tab_a中的数据才能关闭)    
        <jsp:useBean id="db" scope="page" class="db"/>      ResultSet rs_jsp_a = null;
        ResultSet rs_jsp_b = null;    rs_jsp_a = db.ExecSelect("select * from tab_a");
        rs_jsp_a.next();
        
        rs_jsp_b = db.ExecSelect("select * from tab_b");
        rs_jsp_b.next();    rs_jsp_a.close();    //这两句话有没有必要加上呢?
        rs_jsp_a = null;     //这两句话有没有必要加上呢?  
        rs_jsp_b.close();    //这两句话有没有必要加上呢?
        rs_jsp_b = null;     //这两句话有没有必要加上呢?      db.closedb();
        想请问,在这个运行过程中,rs_jsp_a、rs_jsp_b、db.rs这三者是怎样的联系关系呢? java是怎样安排内存并回收内存的呢?
        这种实现方法会不会存在内存泄漏呢?
      

  3.   

    好象没必要=null,虚拟机会自动回收.
      

  4.   

    无论ResultSet, Statement, Connection在使用完之后都要close, 但不需要设置成null. 
    ResultSet  rs;只是定义一个对象, 当 stmt.executeQuery("select * from tab_a");就生成了一个新的对象, 每个对象都要关闭, 因此, 在rs变量给另外使用前, 原来的对象需要关闭.关键: java一切都是对象
    1.
    Connection conn = null;
    Statement  stmt = null;
    ResultSet  rs   = null;
    try {
      conn = getConnection(  );  //此处省略获取connection的具体代码
      stmt = conn.createStatement();
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
    }
    catch(SQLException ex) {
    }
    finally{
          if (   rs != null ) {   rs.close();  rs = null;  }
          if ( stmt != null ) { stmt.close(); stmt = null; }
          if ( conn != null ) { conn.close(); conn = null; }
    }2.
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();    rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();  
      
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close(); 3.
        rs_jsp_a.close();    //这两句话有必要加上
        rs_jsp_a = null;     //这两句话有必要加上
        rs_jsp_b.close();    //这两句话有必要加上
        rs_jsp_b = null;     //这两句话有必要加上
      

  5.   

    把对象赋值null,就等于取消了这个变量指针与这个变量所在的内存块之间的联系,java的垃圾回收器在回收资源时,优先处理没有指针指向的内存块的。所以,设为null可以起到加速被回收的作用。
      

  6.   

    http://www.ieee.org.cn/dispbbs.asp?boardID=49&ID=10232来自SMTH的讨论
      

  7.   

    谢谢各位!
    请问Jimmy,terry_yip
    2.
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();    rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close();  
      
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close(); 
    那应该是申请了三次内存吧?请看下面的问题:
      第1次,rs指向select * from tab_a的内存区,简称mem_a ,rs.close()后关闭该内存块,虽然可以被回收,但由于rs不等于null,rs指针仍指向该内存区域,所以被垃圾回收的优先级别不高。  
      第2次,rs指向select * from tab_b的内存区,简称mem_b ,此时rs指针是不是从mem_a移动到mem_b,而mem_a成为没有被引用的完全close的内存块从而具备了较高被回收的优先级别呢?
    而rs.close()之后,mem_b成为被关闭的但是被rs引用的内存块,可被垃圾回收但优先级不高?
      第3次,rs指向select * from tab_c的内存区,简称mem_c,此时rs指针移动到mem_c,mem_b成为没有被引用的完全close的内存块从而具备了较高被回收的优先级别呢?    如此循环反复??
      
      

  8.   

    2.
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      //rs.close();    rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      //rs.close();  
      
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
      rs.close(); 
    那是不是前两次的内存没有被关闭而不能被回收造成内存泄漏啊?
      

  9.   

    不执行完不会回收,不设置为null回收也许会滞后!
    最后:jsp里面这样写是结构很垃圾的,根本不会这么写。想这些连接和事务的管理完全可以写一个AOP动态植入
      

  10.   

    首先,java的垃圾回收是不确定的,你把rs设置为null系统并不是立即回收内存,只有等到垃圾回收运行的时候才回收。
    其次,有些资源必须在使用后被关闭,如数据库连接,文件,等等,垃圾回收是不会对此类资源回收的,这个一般会造成内存泄漏。但是如果关闭后,就可以被正常回收,是否设置为null其实并无关紧要,只是加快了垃圾回收的运行速度。
    最后,程序没有办法控制内存被垃圾回收的优先级
      

  11.   

    wmzsl:请不要这样批判jsp里面这样写是结构很垃圾的,首先很多教材里是这么写的,再说历史上很多代码也是这样的。但是我现在想求证的是java中基本的问题,也是很重要的问题,才能判断代码如何修改更省时有效,也许AOP动态植入是很好的方法,但我想知其然知其所以然啊
      rs   = stmt.executeQuery("select * from tab_a");
      if ( rs.next() ) out.println(rs.getString(1));
      //rs.close();  
      rs   = stmt.executeQuery("select * from tab_b");
      if ( rs.next() ) out.println(rs.getString(1));
      //rs.close();   
      rs   = stmt.executeQuery("select * from tab_c");
      if ( rs.next() ) out.println(rs.getString(1));
    这三次操作java是怎样使用内存的?rs.close()和rs=null对内存引用有什么影响?对内存资源高效回收(或者防止内存泄漏)到底应该处理更合适?
      

  12.   

    没错,是这样的。但有个问题你理解有点偏差。rs.close();和光是rs=null;是不等价的,其实rs所指向的内存区间,并不只是一块的,从这一块,又连接到另外的很多块(只为初始化一个rs参对象,会附带始终化很多其它对象的),如果视觉化的话,是呈幅射形的,你用rs.close()的话,从这一块幅射出去的其它内存块,也会被标记为“可被回收”,如果rs=null,只是rs指向的那一块会被回收。不过具理的原理,跟那个数据库的驱动程序有关系的。
      

  13.   

    今天还是第一次听到内存块幅射这样的比喻。那当然rs.close()是必须的操作,rs=null是后继的操作。以前习惯了rs = stmt.executeQuery("select * from tab_a");rs = stmt.executeQuery("select * from tab_b");rs = stmt.executeQuery("select * from tab_c");等这样连续的用一个rs进行多次查询操作而每次不同的查询之间都没有调用rs.close(),只是最后一次调用rs.close()。那这样应该是前2次的查询操作占用的大批内存都不能被回收了啊?