需求:

将从第三方拉取数据,存入表中,表字段太多(150+字段),要求全量存储。

方法:

使用多线程进行数据存储,加快存储速度。

遇到问题:

由于数据量很大,一页可以查5000条数据,每条数据150个字段,所以获取出来的String会很长,然后因为是一个字符串存储的,所以当前String对象会很大。

由于JVM算法的原因,新生代的垃圾回收是采用复制算法,而复制算法的缺点就是当大量大对象存在的时候会导致回收效率的降低,因为要复制移动对象,新生代为了避免这个缺点所以采用大对象直接进入老年代,防止大对象长时间在新生代存活,影响gc效率。通过 -XX:PretenureSizeThreshold 参数配置,单位大小是字节,比如 -XX:PretenureSizeThreshold =1024,那么就表示超过1kb大小的对象在垃圾回收时直接进入到老年代.

因为String对象数据量太大超过了默认值1KB,所以在堆空间在进行GC时候会将其放进老年代,且String对象特性不能被重复使用,每次赋值的时候都是创建一个新的String对象,这时候当数据量超过了一定量级时候(我的数据量为23万左右),堆内存就会出现内存溢出情况java.lang.OutOfMemoryError: Java heap space错误,或者当GC进行垃圾回收时候java.lang.OutOfMemoryError:GC ****错误。

解决方案:

首先我们需要将线程池(new ThreadPoolExecutor)创建放到循环外,然后在循环里面尽量少用String来接收大量级数据,改用StringBuffer或者StringBuilder,如果没办法必须要使用String来接收大量级数据时,需要在使用完该数据并在进入下一次循环前置空(String = null),这样来提示GC回收此对象即可:

//新建一个线程池
        ExecutorService executor = new ThreadPoolExecutor(5, 20, 0,
                TimeUnit.SECONDS,
                //1.如果使用无界队列,大量提交任务会堆积,造成内存占用太高
//                new LinkedBlockingQueue<Runnable>(),
                //2.应该使用有界队列
                new LinkedBlockingQueue<Runnable>(50),
                //2.加上调用线程任务本身去run
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        List<Future> futureList = new ArrayList<>();
        OEResult<TLyCarInfo> oeResult = new OEResult<>();
        String data = "";
        count = insertData(params, url, "", count, executor, futureList, oeResult, data);

        for (Future future : futureList) {
            try {
                future.get();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
private int insertData(Map<String, Object> params, String url, String pageindex, int num, ExecutorService executor, List<Future> futureList, OEResult<TLyCarInfo> oeResult, java.lang.String post) {
        params.put("pageindex", pageindex);
        post = HttpsUtils.post(params, url);
        oeResult = JSONUtil.toBean(post, new TypeReference<OEResult<TLyCarInfo>>() {
        }, false);
        List<TLyCarInfo> oeDataList = oeResult.getResult();
        post = null;
        num += oeDataList.size();
        //切割list
        List<List<TLyCarInfo>> partition = Lists.partition(oeDataList, 500);
        //多线程执行保存任务
//        partition.stream().map(sub -> CompletableFuture.supplyAsync(() -> tlyCarInfoService.saveBatch(sub), executor)).collect(Collectors.toList());
        for (List<TLyCarInfo> tLyCarInfos : partition) {
            Future future = executor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        tlyCarInfoService.saveBatch(tLyCarInfos, 1000);
                    } catch (Exception e) {
                        System.out.println(e);
                    }
                }
            });
            futureList.add(future);
        }
        if (!oeResult.getInfo().getSdmPageIndex().equals("-1")) {
            System.out.println("sdmPageIndex==========" + oeResult.getInfo().getSdmPageIndex());
            //当sdmPageIndex不为-1时,则还有后续页,需要继续存储
            insertData(params, url, oeResult.getInfo().getSdmPageIndex(), num, executor, futureList, oeResult, post);
        }
        return num;
    }

 此为自用,如有描述不对请多指正