tomcat 频繁触发full gc

服务器:tomcat
简单描述:db中有460w+条记录,在@Service中,使用while true,一次从DAO层中select 出500条,逐条处理,处理完之后再select 出500条,直到select出来是空的list,就break;
问题:由于总共有460w条数据,还是逐条处理,所以程序跑了24小时都没有跑完,有没有可能出现full gc?如果出现full gc,原因可能是什么?
示意代码:

public void doProcess(){
        while (true){
            List<UserDTO> list = myDao.selectUserPage();
            if (list.size()>0){
                for (UserDTO userDTO:list){
                    //处理业务逻辑,每500条大致需要30多秒
                }
            }else {
                break;
            }
        }
    }
阅读 6.6k
9 个回答

少年,我来给你做一道数学题,460w / 500 * 0.5分钟 / 60 = 76.67 小时,所以24小时没处理完是正常的。

如果不看你业务逻辑部分代码,单看你这段逻辑,理论上不会Full GC的(你的内存不至于连500条记录都存储不下的吧),当然,你的程序必须是单线程的,如果是多线程(而且线程数还比较高)则有可能。出于程序健壮性考虑,你的判断语句list.size() > 0最好能改成list != null && list.size() > 0

一般来说的数据库每秒可以读取一万到几万的记录,如果你是从执行一条一次sql语句的结果中取460w记录,应该10分钟之内,就可以读完。所以问题应该是要么myDao.selectUserPage()花了太长时间,要么就像你注释上说的处理业务逻辑,每500条大致需要30多秒。如果是处理业务逻辑太慢,可以用多线程加快速速。我推荐一个框架: Abacus-Util,你可以试一下:

static final SQLExecutor sqlExecutor = new SQLExecutor(JdbcUtil.createDataSource(driver, url, user, password));

// sql 是一次性取出所有结果的SQL,不带分页的。
// 虽然SQL是一次取出所有结果,但由于下面的Stream lazy load,所以结果只是一条一条读到内存里,处理一条就会扔掉,不会把所有的460W记录同时保存在内存。
// parameters 是 sql 参数,可以不写,如果SQL没有参数。
sqlExecutor.stream(UserDTO.class, sql, parameters).run(s -> s.parallel(threadNum).forEach(userDTO -> {
    // 处理业务逻辑
}));

具体还要看你业务逻辑里有没有持有userDTO的引用没有释放,若没有的话应该就不是gc的问题。
建议用jstatck来dump线程快照看看

不会
list重新赋值的时候,之前的值就成了垃圾,会被回收。

最重要的是,数据库是本地还是跨网

还存在一个问题,如果处理能力能达到的话,查出来list若为空,list.size()这句是会报空指针异常的,所以为空判断应该放前面,为空则break。

1 有可能频繁产生full gc,并且由于对象过多无法释放导致oom。 比如下面代码块,通过map又增加了userDTO的引用。

 Map map = ...
 for (userDTO userDTO:list){
    map.put(userDTO.getname(), userDTO);              
 }

2 这种数据量大的计算,可以考虑用存储过程来实现,计算效率会大幅提高。

很低的可能性导致full gc的,以你的逻辑,每次500条的产生对象几乎引用范围在新生代就被回收掉了。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题