Nguyên nhân gây ra lỗi java.lang.StackOverflowError


Câu trả lời:


60

Kiểm tra bất kỳ cuộc gọi lặp lại nào cho các phương thức. Chủ yếu nó được gây ra khi có lệnh gọi đệ quy cho một phương thức. Một ví dụ đơn giản là

public static void main(String... args) {
    Main main = new Main();

    main.testMethod(1);
}

public void testMethod(int i) {
    testMethod(i);

    System.out.println(i);
}

Đây là System.out.println (i); sẽ được đẩy liên tục vào ngăn xếp khi testMethod được gọi.


1
Tôi nghĩ bạn đúng. Nhưng giải pháp của nó là gì. Bởi vì chúng tôi đang tạo ra một phương pháp sử dụng lại nó có nghĩa là chúng tôi cần điều đó. Chúng tôi không muốn thay đổi phương pháp. Vì vậy, làm thế nào có thể loại bỏ lỗi này?
Ajay Sharma

1
hoặc bạn đang vướng vào một vòng lặp vô hạn!
yalematta

@yalematta, bất kỳ phương thức đệ quy nào cũng phải có điều kiện để thoát. Vì vậy, hãy kiểm tra xem phương thức đệ quy của bạn có được triển khai đúng cách hay không và kết thúc tùy thuộc vào một số điều kiện.
Ayaz Alifov

@AjaySharma Chúng tôi cần thiết kế hệ thống của mình để phù hợp với các ranh giới bộ nhớ có sẵn mà chúng tôi đã gán cho JVM. Nếu hệ thống hoạt động khó xử với lỗi sau thì chúng ta cần kiểm tra cơ sở mã của mình.
Thota Srinath

23

Một trong những đối số (tùy chọn) đối với JVM là kích thước ngăn xếp. Đó là -Xss. Tôi không biết giá trị mặc định là gì, nhưng nếu tổng số thứ trên ngăn xếp vượt quá giá trị đó, bạn sẽ gặp lỗi đó.

Nói chung, đệ quy vô hạn là nguyên nhân của điều này, nhưng nếu bạn thấy điều đó, dấu vết ngăn xếp của bạn sẽ có nhiều hơn 5 khung hình.

Hãy thử thêm đối số -Xss (hoặc tăng giá trị của một đối số) để xem điều này có biến mất hay không.


10

Điều thực sự gây ra lỗi java.lang.StackOverflowError thường là đệ quy không chủ ý. Đối với tôi, thường khi tôi định gọi một phương thức siêu cho phương thức bị ghi đè. Chẳng hạn như trong trường hợp này:

public class Vehicle {
    public void accelerate(float acceleration, float maxVelocity) {
        // set the acceleration
    }
}

public class SpaceShip extends Vehicle {
    @Override
    public void accelerate(float acceleration, float maxVelocity) {
        // update the flux capacitor and call super.accelerate
        // oops meant to call super.accelerate(acceleration, maxVelocity);
        // but accidentally wrote this instead. A StackOverflow is in our future.
        this.accelerate(acceleration, maxVelocity); 
    }
}

Đầu tiên, sẽ hữu ích khi biết những gì xảy ra đằng sau hậu trường khi chúng ta gọi một hàm. Các đối số và địa chỉ của nơi phương thức được gọi được đẩy lên ngăn xếp (xem http://en.wikipedia.org/wiki/Stack_(abstract_data_type)#Runtime_memory_management ) để phương thức được gọi có thể truy cập các đối số và để khi phương thức được gọi đã hoàn tất, việc thực thi có thể tiếp tục sau cuộc gọi. Nhưng vì chúng tôi đang gọi this.accelerate (tăng tốc, maxVelocity) một cách đệ quy (đệ quy lỏng lẻo khi một phương thức gọi chính nó. Để biết thêm thông tin, hãy xem http://en.wikipedia.org/wiki/Recursion_(computer_science)) chúng ta đang ở trong một tình huống được gọi là đệ quy vô hạn và chúng ta tiếp tục xếp chồng các đối số và địa chỉ trả về trên ngăn xếp cuộc gọi. Vì ngăn xếp cuộc gọi có kích thước hữu hạn, cuối cùng chúng ta sẽ hết dung lượng. Việc hết dung lượng trên ngăn xếp cuộc gọi được gọi là tràn. Điều này là do chúng tôi đang cố gắng sử dụng nhiều không gian ngăn xếp hơn chúng ta có và dữ liệu tràn ngăn xếp theo đúng nghĩa đen. Trong ngôn ngữ lập trình Java, điều này dẫn đến ngoại lệ thời gian chạy java.lang.StackOverflow và sẽ ngay lập tức tạm dừng chương trình.

Ví dụ trên được đơn giản hóa một chút (mặc dù nó xảy ra với tôi nhiều hơn tôi muốn thừa nhận.) Điều tương tự có thể xảy ra trong một vòng xoay hơn về cách làm cho nó khó theo dõi hơn một chút. Tuy nhiên, nói chung, StackOverflow thường khá dễ giải quyết khi nó xảy ra.

Về lý thuyết, cũng có thể xảy ra tràn ngăn xếp mà không cần đệ quy, nhưng trên thực tế, nó dường như là một sự kiện khá hiếm.


8

Những gì là java.lang.StackOverflowError

Lỗi java.lang.StackOverflowErrorđược đưa ra để chỉ ra rằng ngăn xếp của ứng dụng đã cạn kiệt, do đệ quy sâu, tức là chương trình / tập lệnh của bạn đệ quy quá sâu.

Chi tiết

Lớp StackOverflowErrormở rộng VirtualMachineErrorcho biết rằng JVM đã hoặc đã hết tài nguyên và không thể hoạt động thêm. Các VirtualMachineErrorkéo dài các Errorlớp được sử dụng để chỉ những vấn đề nghiêm trọng mà một ứng dụng không nên bắt. Một phương thức có thể không khai báo những lỗi như vậy trong throwmệnh đề của nó vì những lỗi này là những điều kiện bất thường không bao giờ được mong đợi xảy ra.

Một ví dụ

Minimal, Complete, and Verifiable Example :

package demo;

public class StackOverflowErrorExample {

    public static void main(String[] args) 
    {
        StackOverflowErrorExample.recursivePrint(1);
    }

    public static void recursivePrint(int num) {
        System.out.println("Number: " + num);

        if(num == 0)
            return;
        else
            recursivePrint(++num);
    }

}

Đầu ra bảng điều khiển

Number: 1
Number: 2
.
.
.
Number: 8645
Number: 8646
Number: 8647Exception in thread "main" java.lang.StackOverflowError
    at java.io.FileOutputStream.write(Unknown Source)
    at java.io.BufferedOutputStream.flushBuffer(Unknown Source)
    at java.io.BufferedOutputStream.flush(Unknown Source)
    at java.io.PrintStream.write(Unknown Source)
    at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source)
    at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source)
    at sun.nio.cs.StreamEncoder.flushBuffer(Unknown Source)
    at java.io.OutputStreamWriter.flushBuffer(Unknown Source)
    at java.io.PrintStream.newLine(Unknown Source)
    at java.io.PrintStream.println(Unknown Source)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:11)
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)
    .
    .
    .
    at demo.StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:16)

Giải thích

Khi một lệnh gọi hàm được gọi bởi Ứng dụng Java, một khung ngăn xếp được cấp phát trên ngăn xếp cuộc gọi . Hàm stack framechứa các tham số của phương thức được gọi, các tham số cục bộ của nó và địa chỉ trả về của phương thức. Địa chỉ trả về biểu thị điểm thực thi mà từ đó, việc thực thi chương trình sẽ tiếp tục sau khi phương thức được gọi trả về. Nếu không có không gian cho một khung ngăn xếp mới thì StackOverflowErrorsẽ bị Máy ảo Java (JVM) ném.

Trường hợp phổ biến nhất có thể làm cạn kiệt ngăn xếp của ứng dụng Java là đệ quy. Trong đệ quy, một phương thức gọi chính nó trong quá trình thực thi của nó. Recursionmột trong những kỹ thuật lập trình có mục đích chung mạnh mẽ nhất, nhưng phải được sử dụng một cách thận trọng StackOverflowErrorđể tránh điều này xảy ra.

Người giới thiệu


4

Khi một lệnh gọi hàm được gọi bởi một ứng dụng Java, một khung ngăn xếp được phân bổ trên ngăn xếp cuộc gọi. Khung ngăn xếp chứa các tham số của phương thức được gọi, các tham số cục bộ của nó và địa chỉ trả về của phương thức.

Địa chỉ trả về biểu thị điểm thực thi mà từ đó, việc thực thi chương trình sẽ tiếp tục sau khi phương thức được gọi trả về. Nếu không có không gian cho khung ngăn xếp mới thì Lỗi StackOverflowError sẽ được Máy ảo Java (JVM) ném ra .

Trường hợp phổ biến nhất có thể làm cạn kiệt ngăn xếp của ứng dụng Java là đệ quy.

Xin vui lòng xem

Cách giải quyết lỗi StackOverflowError


3

Giải pháp cho người dùng Hibernate khi phân tích cú pháp dữ liệu:

Tôi gặp lỗi này vì tôi đang phân tích cú pháp danh sách các đối tượng được ánh xạ ở cả hai bên @OneToMany@ManyToOnetới json bằng cách sử dụng jackson, điều này gây ra vòng lặp vô hạn.

Nếu bạn đang ở trong tình huống tương tự, bạn có thể giải quyết điều này bằng cách sử dụng @JsonManagedReference@JsonBackReferencechú thích.

Các định nghĩa từ API:

  • JsonManagedReference ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonManagedReference.html ):

    Chú thích được sử dụng để chỉ ra rằng thuộc tính chú thích là một phần của liên kết hai chiều giữa các trường; và vai trò của nó là liên kết "mẹ" (hoặc "chuyển tiếp"). Loại giá trị (lớp) của thuộc tính phải có một thuộc tính tương thích duy nhất được chú thích bằng JsonBackReference. Liên kết được xử lý sao cho thuộc tính được chú thích bằng chú thích này được xử lý bình thường (bình thường được đăng nhiều kỳ, không có cách xử lý đặc biệt nào đối với việc giải mã hóa); đó là tham chiếu ngược phù hợp yêu cầu xử lý đặc biệt

  • JsonBackReference: ( https://fasterxml.github.io/jackson-annotations/javadoc/2.5/com/fasterxml/jackson/annotation/JsonBackReference.html ):

    Chú thích được sử dụng để chỉ ra rằng thuộc tính liên kết là một phần của liên kết hai chiều giữa các trường; và vai trò của nó là liên kết "con" (hoặc "quay lại"). Loại giá trị của thuộc tính phải là bean: nó không thể là Bộ sưu tập, Bản đồ, Mảng hoặc kiểu liệt kê. Liên kết được xử lý sao cho thuộc tính được chú thích bằng chú thích này không được tuần tự hóa; và trong quá trình giải mã hóa, giá trị của nó được đặt thành ví dụ có liên kết "được quản lý" (chuyển tiếp).

Thí dụ:

Owner.java:

@JsonManagedReference
@OneToMany(mappedBy = "owner", fetch = FetchType.EAGER)
Set<Car> cars;

Car.java:

@JsonBackReference
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "owner_id")
private Owner owner;

Một giải pháp khác là sử dụng @JsonIgnoremà sẽ chỉ đặt null cho trường.


2

Tôi đã tạo một chương trình với hibernate, trong đó tôi tạo hai lớp POJO, cả hai đều có một đối tượng là thành viên dữ liệu của nhau. Khi ở phương thức chính, tôi cố gắng lưu chúng trong cơ sở dữ liệu, tôi cũng gặp lỗi này.

Điều này xảy ra vì cả hai lớp đang tham chiếu lẫn nhau, do đó tạo ra một vòng lặp gây ra lỗi này.

Vì vậy, hãy kiểm tra xem có bất kỳ loại mối quan hệ nào như vậy tồn tại trong chương trình của bạn hay không.


1

Ngoại lệ Tràn ngăn xếp có thể xảy ra khi ngăn xếp luồng tiếp tục tăng kích thước cho đến khi đạt đến giới hạn tối đa.

Điều chỉnh các tùy chọn Kích thước ngăn xếp (Xss và Xmso) ...

Tôi khuyên bạn nên xem liên kết này: http://www-01.ibm.com/support/docview.wss?uid=swg21162896 Có nhiều nguyên nhân có thể gây ra lỗi StackOverflowError, như bạn có thể thấy trong liên kết ....


Các câu trả lời chỉ liên kết thường không được chấp nhận; liên kết bị phá vỡ sẽ làm mất hiệu lực hoàn toàn câu trả lời. Vui lòng cung cấp một số ngữ cảnh, mã và giải thích về câu trả lời thay vì chỉ liên kết.
Jay

0

Trong trường hợp của tôi, tôi có hai hoạt động. Trong hoạt động thứ hai, tôi đã quên đặt super trên phương thức onCreate.

super.onCreate(savedInstanceState);

Ngay cả khi đó là một cách khả thi để nâng cao a StackOverflowError, tôi không cho rằng nó đang trả lời câu hỏi. Tôi nghĩ rằng một câu trả lời thích hợp nên liệt kê các cách khác để có được ngoại lệ này thay vì sử dụng quá nhiều đệ quy hoặc nói rằng chắc chắn không có cách nào khác để có được ngoại lệ đó ngoại trừ việc ném nó theo cách thủ công.
JojOatXGME 19/02/17
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.