java.lang.StackOverflowError解決


在使用JPA的倉儲repository進行查詢時,經常用到findAllbyId的方法: repository.findAllbyId()

但如果像下面的代碼,當list的size量太大的話,就會報棧溢出的的錯誤:java.lang.StackOverflowError

@RequestMapping("/stackOverFlow")
    public Integer stackOverFlow() {

        List<String> ids = new ArrayList<>();
        for (int i = 0; i < 5000; i++) {
            ids.add("123123123123");
        }
        List<BillDO> allById = dwBillRepository.findAllById(ids);

        return allById.size();
    }

報錯信息如下:

Caused by: java.lang.StackOverflowError
    at antlr.BaseAST.toString(BaseAST.java:333) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:341) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]
    at antlr.BaseAST.toStringList(BaseAST.java:347) ~[antlr-2.7.7.jar:?]

原因就是在拼sql時方法入棧太深,超過了jvm允許的最大深度,也就是遞歸調用的太深了。

public String toStringList() {
        String var2 = "";
        if (this.getFirstChild() != null) {
            var2 = var2 + " (";
        }

        var2 = var2 + " " + this.toString();
        if (this.getFirstChild() != null) {
            var2 = var2 + ((BaseAST)this.getFirstChild()).toStringList();
        }

        if (this.getFirstChild() != null) {
            var2 = var2 + " )";
        }

        if (this.getNextSibling() != null) {
            var2 = var2 + ((BaseAST)this.getNextSibling()).toStringList();
        }

        return var2;
    }

 

解決方法就是不要遞歸的太深。或者調整JVM參數棧大小默認為1m,可以調整到10m,看看不能解決問題,但這樣做不推薦。會影響線程數,從而影響系統性能。

具體到上面的問題就是一次不要查太多的數據。如果in的數量有5000,我們就分開查詢一次只查1000,查5次。再把結果組合在一起。

如果每個業務都單獨寫的話,就太麻煩了,可以寫個公共的方法,如下: JPA_QUERY_LIST_MAX_SIZE是一個常量數據值,如1000個查一次。這里使用了並行查詢,查詢效率更高。

public <T> List<T> findAll(List<String> ids, Function<List<String>, List<T>> func) {

        List<List<String>> idGroups = Lists.partition(ids, JPA_QUERY_LIST_MAX_SIZE);
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(idGroups.size());
executor.setKeepAliveTime(10, TimeUnit.SECONDS);//解決線程不退出的問題
executor.allowCoreThreadTimeOut(true);


return idGroups .stream() .collect(ParallelCollectors .parallelToList(i -> func.apply(i), executor, idGroups.size())) .join() .stream() .flatMap(Collection::stream) .collect(toList()); }

以上代碼利用如一個第三方的庫:需要引用:

<dependency>
            <groupId>com.pivovarit</groupId>
            <artifactId>parallel-collectors</artifactId>
            <version>1.1.0</version>
        </dependency>

 

這里引出一個題外話,如果in查詢數據量很大的話,可能會導致索引失效的問題。需要重點看一下。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM