Điều gì có thể gây ra a java.lang.StackOverflowError
? Bản in chồng mà tôi nhận được không sâu lắm (chỉ có 5 phương pháp).
Câu trả lời:
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.
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.
Đ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.
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.
Lớp StackOverflowError
mở rộng VirtualMachineError
cho biết rằng JVM đã hoặc đã hết tài nguyên và không thể hoạt động thêm. Các VirtualMachineError
kéo dài các Error
lớ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 throw
mệ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.
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);
}
}
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)
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 frame
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 một khung ngăn xếp mới thì StackOverflowError
sẽ 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ó. Recursion
mộ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.
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
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
và @ManyToOne
tớ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
và @JsonBackReference
chú 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 @JsonIgnore
mà sẽ chỉ đặt null cho trường.
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.
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 ....
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);
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.