我要从数据库中取数据,大概有一千多万条记录,要把全部的结果保存到一个文件中,其中有个字段custName是存放姓名的,数据库的字符集是ISO-8859-1,要想得到中文还得做转换new String(rs.getString("custName").trim().getBytes("iso-8859-1"),"GBK");看了一些文章,说用new方法就又产生了一个对象,原来的对象所占的内存就只能等JAVA的垃圾回收机制处理了,所以我的程序一运行,内存就狂涨,取数的速度就不断慢下来了。请教各位高手,有什么好办法?
我的程序主要的部分如下:
sql="select * from kh";
String colvalue="";
rs=stmt.executeQuery(sql);
while(rs.next()){
  for(int i=1;i<60;i++){
    if(i!=3){
      colvalue=rs.getString(i);
    }else{
      colvalue=new String(rs.getString("custName").trim().getBytes("iso-8859-1"),"GBK");
    }
    pw.print(colvalue+"|");
  }
  pw.println("");
}
rs.close();
另外,有没有办法可以判断String的值是不是中文?

解决方案 »

  1.   

    分N多次吧。LZ可以先取几十上百条记录。要不你的机子会钢蹦的
      

  2.   

    没法分页取啊,数据库是INFORMIX,好象不支持分页取。
      

  3.   

        這位兄弟可以採用存儲過各分頁方法,這樣可能會快點!!!
    在JSP頁面調用數據庫裡面的存儲過程程序
    存儲過程代碼如下:
    CREATE Proc p_show
    @QueryStr varchar(8000), --表名、視圖名、查詢語句
    @PageSize int=10,   --每頁的大小(行數)
    @PageCurrent int=1,   --要顯示的頁
    @FdShow varchar (8000)='', --要顯示的欄位列表,如果查詢結果有標識欄位,需要指定此值,且不包含標識欄位
    @FdOrder nvarchar (3000)='' --排序欄位列表
    as
    declare @FdName nvarchar(550) --表中的主鍵或表、臨時表中的標識列名
     ,@Id1 varchar(80),@Id2 varchar(80) --開始和結束的記錄號
     ,@Obj_ID int    --對象ID
    --表中有複合主鍵的處理
    declare @strfd nvarchar(4000) --複合主鍵列表
     ,@strjoin varchar(8000) --連接欄位
     ,@strwhere nvarchar(4000) --查詢條件select @Obj_ID=object_id(@QueryStr)
     ,@FdShow=case isnull(@FdShow,'') when '' then ' *' else ' '+@FdShow end
     ,@FdOrder=case isnull(@FdOrder,'') when '' then '' else ' order by '+@FdOrder end
     ,@QueryStr=case when @Obj_ID is not null then ' '+@QueryStr else ' ('+@QueryStr+') a' end
    --如果顯示第一頁,可以直接用top來完成
    if @PageCurrent=1 
    begin
     select @Id1=cast(@PageSize as varchar(50))
     exec('select top '+@Id1+@FdShow+' from '+@QueryStr+@FdOrder)
     return
    end
    --如果是表,則檢查表中是否有標識更或主鍵
    if @Obj_ID is not null and objectproperty(@Obj_ID,'IsTable')=1
    begin
     select @Id1=cast(@PageSize as varchar(50))
      ,@Id2=cast((@PageCurrent-1)*@PageSize as varchar(50))
     select @FdName=name from syscolumns where id=@Obj_ID and status=0x80
     if @@rowcount=0   --如果表中無標識列,則檢查表中是否有主鍵
     begin
      if not exists(select 1 from sysobjects where parent_obj=@Obj_ID and xtype='PK')
       goto lbusetemp  --如果表中無主鍵,則用臨時表處理
      select @FdName=name from syscolumns where id=@Obj_ID and colid in(
       select colid from sysindexkeys where @Obj_ID=id and indid in(
        select indid from sysindexes where @Obj_ID=id and name in(
         select name from sysobjects where xtype='PK' and parent_obj=@Obj_ID
       )))
      if @@rowcount>1  --檢查表中的主鍵是否為複合主鍵
      begin
       select @strfd='',@strjoin='',@strwhere=''
       select @strfd=@strfd+',['+name+']'
        ,@strjoin=@strjoin+' and a.['+name+']=b.['+name+']'
        ,@strwhere=@strwhere+' and b.['+name+'] is null'
        from syscolumns where id=@Obj_ID and colid in(
        select colid from sysindexkeys where @Obj_ID=id and indid in(
         select indid from sysindexes where @Obj_ID=id and name in(
          select name from sysobjects where xtype='PK' and parent_obj=@Obj_ID
        )))
       select @strfd=substring(@strfd,2,2000)
        ,@strjoin=substring(@strjoin,5,4000)
        ,@strwhere=substring(@strwhere,5,4000)
       goto lbusepk
      end
     end
    end
    else
     goto lbusetemp
    /*--使用標識列或主鍵為單一欄位的處理方法--*/
    lbuseidentity: 
      print('select top '+@Id1+@FdShow+' from '+@QueryStr
      +' where '+@FdName+' not in(select top '
      +@Id2+' '+@FdName+' from '+@QueryStr+@FdOrder
      +')'+@FdOrder
      )
     exec('select top '+@Id1+@FdShow+' from '+@QueryStr
      +' where '+@FdName+' not in(select top '
      +@Id2+' '+@FdName+' from '+@QueryStr+@FdOrder
      +')'+@FdOrder
      )
     return 
    /*--表中有複合主鍵的處理方法--*/
    lbusepk:  
     exec('select '+@FdShow+' from(select top '+@Id1+' a.* from
      (select top 100 percent * from '+@QueryStr+@FdOrder+') a
      left join (select top '+@Id2+' '+@strfd+' 
      from '+@QueryStr+@FdOrder+') b on '+@strjoin+'
      where '+@strwhere+') a'
      )
    print ('select '+@FdShow+' from(select top '+@Id1+' a.* from
      (select top 100 percent * from '+@QueryStr+@FdOrder+') a
      left join (select top '+@Id2+' '+@strfd+' 
      from '+@QueryStr+@FdOrder+') b on '+@strjoin+'
      where '+@strwhere+') a'
      )
     return
    /*--用臨時表處理的方法--*/
    lbusetemp:  
    select @FdName='[ID_'+cast(newid() as varchar(80))+']'
     ,@Id1=cast(@PageSize*(@PageCurrent-1) as varchar(50))
     ,@Id2=cast(@PageSize*@PageCurrent-1 as varchar(50))
    exec('select '+@FdName+'=identity(int,0,1),'+@FdShow+'
      into #tb from'+@QueryStr+@FdOrder+'
     select '+@FdShow+' from #tb where '+@FdName+' between '
     +@Id1+' and '+@Id2
     )
    print ('select '+@FdName+'=identity(int,0,1),'+@FdShow+'
      into #tb from'+@QueryStr+@FdOrder+'
     select '+@FdShow+' from #tb where '+@FdName+' between '
     +@Id1+' and '+@Id2
     )
    GO
    在頁面中調用代碼如下:
    public String selectRS_ERP() {
    String str = "{call p_show('" + this.sql + "'," + intPageSize + ","
    + this.intPage + ",'','" + this.orderby + "')}";
    return selectRSERP(str);
    }
    stmt = conn.prepareCall(str);
    rs2 = stmt.executeQuery();
    ResultSetMetaData rsmd = rs2.getMetaData();
    int numberOfColumns = rsmd.getColumnCount();
      

  4.   

        這位兄弟可以採用存儲過各分頁方法,這樣可能會快點!!!
    在JSP頁面調用數據庫裡面的存儲過程程序
    存儲過程代碼如下:
    CREATE Proc p_show
    @QueryStr varchar(8000), --表名、視圖名、查詢語句
    @PageSize int=10,   --每頁的大小(行數)
    @PageCurrent int=1,   --要顯示的頁
    @FdShow varchar (8000)='', --要顯示的欄位列表,如果查詢結果有標識欄位,需要指定此值,且不包含標識欄位
    @FdOrder nvarchar (3000)='' --排序欄位列表
    as
    declare @FdName nvarchar(550) --表中的主鍵或表、臨時表中的標識列名
     ,@Id1 varchar(80),@Id2 varchar(80) --開始和結束的記錄號
     ,@Obj_ID int    --對象ID
    --表中有複合主鍵的處理
    declare @strfd nvarchar(4000) --複合主鍵列表
     ,@strjoin varchar(8000) --連接欄位
     ,@strwhere nvarchar(4000) --查詢條件select @Obj_ID=object_id(@QueryStr)
     ,@FdShow=case isnull(@FdShow,'') when '' then ' *' else ' '+@FdShow end
     ,@FdOrder=case isnull(@FdOrder,'') when '' then '' else ' order by '+@FdOrder end
     ,@QueryStr=case when @Obj_ID is not null then ' '+@QueryStr else ' ('+@QueryStr+') a' end
    --如果顯示第一頁,可以直接用top來完成
    if @PageCurrent=1 
    begin
     select @Id1=cast(@PageSize as varchar(50))
     exec('select top '+@Id1+@FdShow+' from '+@QueryStr+@FdOrder)
     return
    end
    --如果是表,則檢查表中是否有標識更或主鍵
    if @Obj_ID is not null and objectproperty(@Obj_ID,'IsTable')=1
    begin
     select @Id1=cast(@PageSize as varchar(50))
      ,@Id2=cast((@PageCurrent-1)*@PageSize as varchar(50))
     select @FdName=name from syscolumns where id=@Obj_ID and status=0x80
     if @@rowcount=0   --如果表中無標識列,則檢查表中是否有主鍵
     begin
      if not exists(select 1 from sysobjects where parent_obj=@Obj_ID and xtype='PK')
       goto lbusetemp  --如果表中無主鍵,則用臨時表處理
      select @FdName=name from syscolumns where id=@Obj_ID and colid in(
       select colid from sysindexkeys where @Obj_ID=id and indid in(
        select indid from sysindexes where @Obj_ID=id and name in(
         select name from sysobjects where xtype='PK' and parent_obj=@Obj_ID
       )))
      if @@rowcount>1  --檢查表中的主鍵是否為複合主鍵
      begin
       select @strfd='',@strjoin='',@strwhere=''
       select @strfd=@strfd+',['+name+']'
        ,@strjoin=@strjoin+' and a.['+name+']=b.['+name+']'
        ,@strwhere=@strwhere+' and b.['+name+'] is null'
        from syscolumns where id=@Obj_ID and colid in(
        select colid from sysindexkeys where @Obj_ID=id and indid in(
         select indid from sysindexes where @Obj_ID=id and name in(
          select name from sysobjects where xtype='PK' and parent_obj=@Obj_ID
        )))
       select @strfd=substring(@strfd,2,2000)
        ,@strjoin=substring(@strjoin,5,4000)
        ,@strwhere=substring(@strwhere,5,4000)
       goto lbusepk
      end
     end
    end
    else
     goto lbusetemp
    /*--使用標識列或主鍵為單一欄位的處理方法--*/
    lbuseidentity: 
      print('select top '+@Id1+@FdShow+' from '+@QueryStr
      +' where '+@FdName+' not in(select top '
      +@Id2+' '+@FdName+' from '+@QueryStr+@FdOrder
      +')'+@FdOrder
      )
     exec('select top '+@Id1+@FdShow+' from '+@QueryStr
      +' where '+@FdName+' not in(select top '
      +@Id2+' '+@FdName+' from '+@QueryStr+@FdOrder
      +')'+@FdOrder
      )
     return 
    /*--表中有複合主鍵的處理方法--*/
    lbusepk:  
     exec('select '+@FdShow+' from(select top '+@Id1+' a.* from
      (select top 100 percent * from '+@QueryStr+@FdOrder+') a
      left join (select top '+@Id2+' '+@strfd+' 
      from '+@QueryStr+@FdOrder+') b on '+@strjoin+'
      where '+@strwhere+') a'
      )
    print ('select '+@FdShow+' from(select top '+@Id1+' a.* from
      (select top 100 percent * from '+@QueryStr+@FdOrder+') a
      left join (select top '+@Id2+' '+@strfd+' 
      from '+@QueryStr+@FdOrder+') b on '+@strjoin+'
      where '+@strwhere+') a'
      )
     return
    /*--用臨時表處理的方法--*/
    lbusetemp:  
    select @FdName='[ID_'+cast(newid() as varchar(80))+']'
     ,@Id1=cast(@PageSize*(@PageCurrent-1) as varchar(50))
     ,@Id2=cast(@PageSize*@PageCurrent-1 as varchar(50))
    exec('select '+@FdName+'=identity(int,0,1),'+@FdShow+'
      into #tb from'+@QueryStr+@FdOrder+'
     select '+@FdShow+' from #tb where '+@FdName+' between '
     +@Id1+' and '+@Id2
     )
    print ('select '+@FdName+'=identity(int,0,1),'+@FdShow+'
      into #tb from'+@QueryStr+@FdOrder+'
     select '+@FdShow+' from #tb where '+@FdName+' between '
     +@Id1+' and '+@Id2
     )
    GO
    在頁面中調用代碼如下:
    public String selectRS_ERP() {
    String str = "{call p_show('" + this.sql + "'," + intPageSize + ","
    + this.intPage + ",'','" + this.orderby + "')}";
    return selectRSERP(str);
    }
    stmt = conn.prepareCall(str);
    rs2 = stmt.executeQuery();
    ResultSetMetaData rsmd = rs2.getMetaData();
    int numberOfColumns = rsmd.getColumnCount();
      

  5.   

    java是由系统管理垃圾内存的,用了new就会在堆里建一个对象,所以如果用new的话,内存不可避免的会耗得很厉害。不过好像有个手动释放垃圾内存的方法,finalize()。我也没用过,具体介绍可以参考http://blog.csdn.net/yzsind/archive/2006/06/24/829556.aspx
      

  6.   

    只有数据库的select权限,没法写存储过程了
      

  7.   

    分页分页!
    拿mysql来说,用limit
    其他各种数据库有自己的分页机制
      

  8.   

    ...finalize只是在gc時系統會調用的一個方法而己,即使是顯式地調用gc,效果也只是告訴一下系統想要gc而己,gc的實際操作時機由系統決定
    一次取一千萬條記錄...我也覺得此做法不好
      

  9.   

    一千万条记录是绝对有的,一年下来7千多万条记录。我就是想问一下将中文的字符串转码(从ISO-8859-1转为GBK)能不能不要产生垃圾。
      

  10.   

    sql="select * from kh"; 
    String colvalue=""; 
    rs=stmt.executeQuery(sql); 
    while(rs.next()){ 
      for(int i=1;i <60;i++){ 
        if(i!=3){ 
          colvalue=rs.getString(i); 
        }else{ 
          colvalue=new String(rs.getString("custName").trim().getBytes("iso-8859-1"),"GBK"); 
        } 
        pw.print(colvalue+"|"); 
      } 
      pw.println(""); 

    rs.close(); 

    代码里面加一个计数变量,只固定取N个值,等需要访问后面的值时再去数据库中查询后面的值(也就是分页)
      

  11.   

    一次性取1w条以上是很常见的事情啊,例如csv下载,要求下载符合检索条件的所有记录,
    这个就没法分页处理了。
    如果数据量太大负载高也可以设置tomcat内存分配机制,调大些就行了。
    (只试过一两万的,再大的估计不可行了)
      

  12.   

    别理那些说分页的,完全是胡说八道,用个游标就可以解决的事,搞什么分页。分页仅仅在取前几页的时候性能好。像你这样的数据量,取5万行以后的页数,每页花的时间和取全部数据的时间是相同的。new是会产生对象,可是用完了也就可以回收了,内存增长是正常的,至于说速度慢下来,是你的初始内存设置过小,导致垃圾回收器不停的运作。只要把初始内存设大一点就行了。
      

  13.   

    顶18楼这么多数据存在一个文件中,当然不能一次都取到内存中,把文件设置成可追加模式,一次取5000或者10000条,加到文件后面。执行1000次就有1千万了。嫌文件太大的话还可以把文件分成若干个小文件。数据库不支持分页查询也不要紧,rs.next()一条一条过就可以了。另外不要使用CSV文件,它只能容纳65536行。
      

  14.   

    楼主的意思是说new String()次数多,导致垃圾回收次数多,所有速度才慢的?
    估计不是
      

  15.   

    数据库字符集只能设置为ISO-8859-1。
      

  16.   

    不new的话,应该不能进行编码转换,所以这点内存的消耗应该是必须的吧 ?
    GC引起CPU和内存使用率过高,只能适当调整初始内存设置和最大内存设置.因为GC是根据服务器的这个设置值来进行垃圾回收的.
    还有就是看看能不能修改DB的内存分配. 估计还是没有权限.. 
      

  17.   


    启动参数中加入:
    -Xms256m -Xmx512m
      

  18.   

    那有没有办法判断一个字符串里有没有中文,这个字符串是用rs.getString()得到的,数据库字符集是ISO-8859-1
      

  19.   

    我也遇到过数据很多的问题,记得就是用数据库分页查询的方法做的,
    具体不太清楚,lZ可以看看这个,
    http://www.blogjava.net/phe441/archive/2006/12/11/87042.html
      

  20.   

    建议如下:
    1、将现有的数据库中custName字段字符转换成GBK,这样获取时不用转码。应用系统中在保存记录时将custName存入GBK内容。
    2、文件采用追加方式。
    3、在pw.print(colvalue+"|"); 代码后增加“colvalue=null”。使gc时减少判断。
      

  21.   


    sql="select * from kh";
    String colvalue="";
    rs=stmt.executeQuery(sql);
    while(rs.next()){
      for(int i=1;i <60;i++){
        if(i!=3){
          colvalue=rs.getString(i);
        }else{
          colvalue=new String(rs.getString("custName").trim().getBytes("iso-8859-1"),"GBK");
        }
        pw.print(colvalue+"|");
        
      }
      pw.println("");
      // 你在打印完以后已经没有指向new出来的String的引用了,
      // 所以你可以在这里加上主动调用垃圾收集的代码试试,我没测试过哦^-^
      System.gc();
    }
    rs.close(); 
      

  22.   

    不在new 的问题,在与 你没有分页。
    我查了查 你的数据库是不是支持下面的分页语句。
    select   first   n   from   TableName   order   by   fieldName   Asc
      

  23.   

    你应该优化你的sql 让他在数据库中查询时一次查询50条或100条这样处理
    还有String colvalue="";这个换成StringBuffer效果能好不少 你用String会产生大量的对象 占用你的内存