Java項(xiàng)目中大批量數(shù)據(jù)查詢(xún)導(dǎo)致OOM的解決
背景
項(xiàng)目中有時(shí)候一次性將大批量數(shù)據(jù)都查出來(lái)到內(nèi)存中導(dǎo)致內(nèi)存占用過(guò)多很可能會(huì)導(dǎo)致內(nèi)存溢出
內(nèi)存溢出的具體原因
在JVM內(nèi)存結(jié)構(gòu)中分為以下幾個(gè)模塊
- 程序計(jì)數(shù)器
- 虛擬機(jī)棧
- 本地方法棧
- 堆內(nèi)存
- 方法區(qū)
程序中的實(shí)例對(duì)象包括從數(shù)據(jù)庫(kù)讀取的數(shù)據(jù)是存在堆內(nèi)存中的。所以這里的OutOfMemoryError是因?yàn)閿?shù)據(jù)量過(guò)大超過(guò)了堆內(nèi)存中設(shè)置的最大內(nèi)存,一次性讀取的數(shù)據(jù)太多導(dǎo)致內(nèi)存容納不下,拋出了內(nèi)存異常
錯(cuò)誤模擬
如果堆的內(nèi)存大小超過(guò) -Xmx 設(shè)定的最大內(nèi)存, 就會(huì)拋出 OutOfMemoryError 異常,先準(zhǔn)備好數(shù)據(jù)在啟動(dòng)項(xiàng)目時(shí)把堆內(nèi)存的最大值設(shè)置小一點(diǎn)就可以進(jìn)行模擬內(nèi)存溢出的異常
搭建一個(gè)本地項(xiàng)目。需求描述:查詢(xún)表call_task中待撥打的數(shù)據(jù)進(jìn)行撥打,call_task中一次可能會(huì)有大批量數(shù)據(jù)需要處理。本次準(zhǔn)備了1萬(wàn)條數(shù)據(jù)
/**
* 查詢(xún)數(shù)據(jù)執(zhí)行撥打
**/
@Override
public void waitingCall() {
List<CallTask> waitingTaskList = list(Wrappers.<CallTask>lambdaQuery()
.eq(CallTask::getCallStatus, "WAITING")
.le(CallTask::getReqTime, new Date())
);
if (CollUtil.isEmpty(waitingTaskList)) {
return;
}
call(waitingTaskList);
}
/**
* 模擬執(zhí)行具體撥打數(shù)據(jù)
**/
private void call(List<CallTask> waitingTaskList) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}啟動(dòng)項(xiàng)目時(shí)將JVM堆內(nèi)存的大小調(diào)整到合適數(shù)值(可以造成內(nèi)存溢出的情況)。本次最大設(shè)置的20m,最小設(shè)置的10m
- -Xmx用來(lái)表示堆的最大內(nèi)存
- -Xms用來(lái)表示堆的最小內(nèi)存
idea設(shè)置方式, 選擇 run/debug configurations 設(shè)置 VM options

問(wèn)題復(fù)現(xiàn)
啟動(dòng)程序執(zhí)行方法,程序出現(xiàn)錯(cuò)誤 java.lang.OutOfMemoryError: Java heap space

解決辦法
既然是數(shù)據(jù)量過(guò)大導(dǎo)致的問(wèn)題,我們可以減少每次查詢(xún)的數(shù)據(jù)來(lái)解決這個(gè)問(wèn)題
使用分頁(yè)查詢(xún),避免一次性查詢(xún)所有的數(shù)據(jù),分批次處理
public void methodOne() {
long page = 1;
long limit = 500;
long total;
do {
Page<CallTask> taskPage = new Page<>(page, limit);
Page<CallTask> pagResult = page(taskPage,
Wrappers.<CallTask>lambdaQuery()
.eq(CallTask::getCallStatus, "WAITING")
.le(CallTask::getReqTime, new Date()));
total = pagResult.getTotal();
List<CallTask> records = pagResult.getRecords();
if (CollUtil.isEmpty(records)) {
break;
}
call(records);
page++;
} while (page * limit < total);
}使用流式查詢(xún)
public void methodTwo() {
Date reqDate = new Date();
LambdaQueryWrapper<CallTask> wrapper = Wrappers.<CallTask>lambdaQuery()
.eq(CallTask::getCallStatus, "WAITING")
.le(CallTask::getReqTime, reqDate);
int batchSize = 1000;
int offset = 0;
while (true) {
List<CallTask> taskList = list(wrapper.last("limit " + offset + "," + batchSize));
if (CollUtil.isEmpty(taskList)) {
break;
}
offset += batchSize;
call(taskList);
}
}流式查詢(xún)和分頁(yè)查詢(xún)的使用場(chǎng)景
分頁(yè)查詢(xún)通常用于展示數(shù)據(jù),如在網(wǎng)頁(yè)上顯示數(shù)據(jù)列表,需要分頁(yè)展示。而流式查詢(xún)通常用于數(shù)據(jù)處理,如在數(shù)據(jù)清洗、數(shù)據(jù)分析等場(chǎng)景中
查詢(xún)數(shù)據(jù)的建議
減少查詢(xún)字段,避免 select *
到此這篇關(guān)于Java項(xiàng)目中大批量數(shù)據(jù)查詢(xún)導(dǎo)致OOM的解決的文章就介紹到這了,更多相關(guān)Java數(shù)據(jù)查詢(xún)導(dǎo)致OOM內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
向Spring IOC 容器動(dòng)態(tài)注冊(cè)bean實(shí)現(xiàn)方式
這篇文章主要為大家介紹了向Spring IOC 容器動(dòng)態(tài)注冊(cè)bean實(shí)現(xiàn)方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
java判斷Long類(lèi)型的方法和實(shí)例代碼
在本篇文章里小編給大家整理的是關(guān)于java判斷Long類(lèi)型的方法和實(shí)例代碼,對(duì)此有需要的朋友們跟著學(xué)習(xí)參考下。2020-02-02
Java使用JDBC向MySQL數(shù)據(jù)庫(kù)批次插入10W條數(shù)據(jù)(測(cè)試效率)
使用JDBC連接MySQL數(shù)據(jù)庫(kù)進(jìn)行數(shù)據(jù)插入的時(shí)候,特別是大批量數(shù)據(jù)連續(xù)插入(100000),如何提高效率呢?今天小編通過(guò)本教程給大家介紹下2016-12-12
詳解Java實(shí)現(xiàn)JSONArray轉(zhuǎn)Map的三種實(shí)現(xiàn)方式
本文主要介紹了Java實(shí)現(xiàn)JSONArray轉(zhuǎn)Map的三種實(shí)現(xiàn)方式,本文只是自己常用的三種,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
SpringBoot中HTTP請(qǐng)求不通的原因級(jí)解決方法
HTTP 請(qǐng)求是指從客戶(hù)端到服務(wù)器的請(qǐng)求消息,對(duì)于一個(gè) Spring Boot 項(xiàng)目而言,服務(wù)器就是 Spring Boot,客戶(hù)端就是用戶(hù)本地的瀏覽器,但是會(huì)遇到SpringBoot HTTP請(qǐng)求不通的問(wèn)題,本文介紹了一些常見(jiàn)問(wèn)題及解決方法,需要的朋友可以參考下2025-02-02
java設(shè)計(jì)模式之簡(jiǎn)單工廠模式簡(jiǎn)述
這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之簡(jiǎn)單工廠模式,簡(jiǎn)單工廠模式的實(shí)質(zhì)是由一個(gè)工廠類(lèi)根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類(lèi)的實(shí)例,感興趣的小伙伴們可以參考一下2016-08-08
基于request獲取訪問(wèn)者真實(shí)IP代碼示例
這篇文章主要介紹了基于request獲取訪問(wèn)者真實(shí)IP代碼示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10

