NullPulumException trong Java không có StackTrace


332

Tôi đã có các phiên bản mã Java của chúng tôi NullPointerException , nhưng khi tôi cố gắng đăng nhập StackTrace (về cơ bản kết thúc cuộc gọi Throwable.printStackTrace()), tất cả những gì tôi nhận được là:

java.lang.NullPointerException

Đã có người khác đi qua này? Tôi đã thử googling cho "java null con trỏ ngăn xếp rỗng" nhưng không bắt gặp bất cứ thứ gì như thế này.


Bối cảnh là gì? Có nhiều chủ đề liên quan? Tôi đã gặp sự cố khi cố gắng lấy dấu vết ngăn xếp của một ngoại lệ trong SwingWorker.
Michael Myers

Không có luồng nào liên quan ở đây, chỉ đơn giản là Java cũ.
Edward Shtern

1
@Bozho - không - không biết cách tái tạo NullPulum chưa.
Edward Shtern


Thông tin thêm về -XX:-OmitStackTraceInFastThrowbản sao: stackoverflow.com/questions/4659151/
Kẻ

Câu trả lời:


406

Có lẽ bạn đang sử dụng JVM HotSpot (ban đầu bởi Sun microsystems, sau này được mua bởi Oracle, một phần của OpenJDK), thực hiện rất nhiều tối ưu hóa. Để lấy lại dấu vết ngăn xếp, bạn cần chuyển tùy chọn -XX:-OmitStackTraceInFastThrowcho JVM.

Tối ưu hóa là khi xảy ra ngoại lệ (thường là NullPulumException) lần đầu tiên, dấu vết ngăn xếp đầy đủ được in và JVM ghi nhớ dấu vết ngăn xếp (hoặc có thể chỉ là vị trí của mã). Khi ngoại lệ đó xảy ra thường xuyên đủ, dấu vết ngăn xếp không được in nữa, cả hai để đạt được hiệu suất tốt hơn và không làm ngập nhật ký với dấu vết ngăn xếp giống hệt nhau.

Để xem cách nó được triển khai trong JVM của HotSpot, hãy lấy một bản sao của nó và tìm kiếm biến toàn cục OmitStackTraceInFastThrow. Lần trước tôi đã xem mã (năm 2019), nó nằm trong tệp graphKit.cpp .


1
Cảm ơn vì tiền hỗ trợ. Bất kỳ ý tưởng nào nếu có bất kỳ vấn đề ẩn nào để vượt qua tùy chọn này (có vẻ khá vô hại miễn là ứng dụng của tôi không có quá nhiều ngoại lệ)?
Edward Shtern

Không có gotchas ẩn mà tôi biết. Khi bạn nhìn vào mã nguồn Hotspot, bạn có thể thấy tùy chọn này chỉ được sử dụng ở một nơi (graphKit.cpp). Và điều đó có vẻ tốt với tôi.
Roland Illig

34
Tôi nghĩ rằng tôi sẽ thêm một chút thông tin rằng khi dấu vết ngăn xếp được tối ưu hóa, đó là vì nó đã được xử lý hoàn toàn ít nhất một lần: jawspeak.com/2010/05/26/ Lỗi
sharakan

1
Tôi đang chạy JVM OpenJDK, phiên bản 1.8.0u171 (Debian 9) và dường như nó cũng chấp nhận -XX:-OmitStackTraceInFastThrowcờ. Tôi vẫn chưa xác nhận nếu đó là lý do tại sao tôi cũng không in dấu vết ngăn xếp (ví dụ: sử dụng e.printStackTrace), nhưng có vẻ như rất có thể. Tôi đã mở rộng câu trả lời để phản ánh khám phá này.
Chris W.

Trong trường hợp của chúng tôi, 125 trường hợp ngoại lệ đầu tiên có dấu vết ngăn xếp và sau đó phần còn lại trong 3 lần quay của tệp nhật ký không có. Câu trả lời này rất hữu ích trong việc tìm ra thủ phạm.
sukhmel

61

Như bạn đã đề cập trong một bình luận, bạn đang sử dụng log4j. Tôi đã phát hiện ra (vô tình) một nơi mà tôi đã viết

LOG.error(exc);

thay vì điển hình

LOG.error("Some informative message", e);

thông qua sự lười biếng hoặc có lẽ chỉ là không nghĩ về nó. Điều không may của việc này là nó không hoạt động như bạn mong đợi. API logger thực sự lấy Object làm đối số đầu tiên, không phải là một chuỗi - và sau đó nó gọi toString () trên đối số. Vì vậy, thay vì nhận được dấu vết ngăn xếp đẹp, nó chỉ in ra toString - trong trường hợp NPE là khá vô dụng.

Có lẽ đây là những gì bạn đang trải nghiệm?


+1: Điều này sẽ giải thích hành vi được mô tả và bạn không phải là người duy nhất phát hiện ra điều này :)
Peter Lang

4
Chúng tôi thực sự có một chính sách tiêu chuẩn là không bao giờ sử dụng biểu mẫu đầu tiên ở trên (LOG.error (exc);) - chúng tôi luôn sử dụng chữ ký 2 tham số để chúng tôi thêm một số câu lệnh mô tả vào nhật ký thay vì chỉ là một stacktrace thô.
Edward Shtern

5
Chắc chắn, nhưng chính sách không có nghĩa là nó luôn luôn được thực thi chính xác! Hình là nó đáng được đề cập, ít nhất.
Steven Schlansker

Đúng, nhưng trong trường hợp này là ;-)
Edward Shtern

28

Chúng ta đã thấy hành vi tương tự trong quá khứ. Hóa ra, vì một số lý do điên rồ, nếu một NullPulumException xảy ra tại cùng một vị trí trong mã nhiều lần, sau một thời gian sử dụngLog.error(String, Throwable) sẽ dừng bao gồm cả dấu vết ngăn xếp đầy đủ.

Hãy thử nhìn lại trong nhật ký của bạn. Bạn có thể tìm ra thủ phạm.

EDIT: lỗi này nghe có vẻ có liên quan, nhưng nó đã được sửa từ lâu rồi có lẽ không phải là nguyên nhân.


2
Lỗi đã được đóng, nhưng cờ -XX: -OmitStackTraceInFastThrow vẫn cần thiết để khắc phục tối ưu hóa hiệu suất.
Joshua Goldberg

Tôi đã nhìn thấy điều này gần đây rất nhiều. Bất kỳ manh mối nào về những gì có thể gây ra điều này, hoặc làm thế nào để khắc phục nó? Hệ thống đăng nhập có thể đã hoạt động trong nhiều ngày và nguyên nhân thực sự đã bị loại bỏ, không bao giờ tìm kiếm tẻ nhạt ...
Pawel Veselov

5
Pawel, bạn đã thử -XX:-OmitStackTraceInFastThrowcờ JVM được đề xuất bởi Joshua chưa? Xem thêm stackoverflow.com/a/2070568/6198 .
Matt Solnit

1
Đây là nó cho chúng tôi. Cảm ơn.
Andrew Cheong

20

Dưới đây là một lời giải thích: Hotspot khiến các ngoại lệ mất dấu vết ngăn xếp trong sản xuất - và cách khắc phục

Tôi đã thử nghiệm nó trên Mac OS X

  • phiên bản java "1.6.0_26"
  • Môi trường thời gian chạy Java (TM) SE (bản dựng 1.6.0_26-b03-383-11A511)
  • Máy chủ 64-bit Java HotSpot (TM) (bản dựng 20.1-b02-383, chế độ hỗn hợp)

    Object string = "abcd";
    int i = 0;
    while (i < 12289) {
        i++;
        try {
            Integer a = (Integer) string;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Đối với đoạn mã cụ thể này, 12288 lần lặp (+ tần số?) Dường như là giới hạn mà JVM đã quyết định sử dụng ngoại lệ preallocated ...


10

exception.toString không cung cấp cho bạn StackTrace, nó chỉ trả về

một mô tả ngắn về ném này. Kết quả là sự kết hợp của:

* the name of the class of this object
* ": " (a colon and a space)
* the result of invoking this object's getLocalizedMessage() method

Sử dụng exception.printStackTracethay thế để xuất StackTrace.


Xin lỗi, tôi viết sai trong bài viết gốc của tôi. Tôi đang đăng nhập thông qua Log4J, công ty này sử dụng printStackTrace ().
Edward Shtern

1
Bạn đã thử sử dụng getStackTrace()để đảm bảo vấn đề không nằm ở logger của bạn chưa?
Peter Lang

1
Nếu bạn đang sử dụng log4j, hãy chắc chắn gửi ngoại lệ như một phần của đối số cho phương thức nhật ký. Tôi sẽ đăng một câu trả lời với điều đó.
Ravi Wallau

@raviaw điểm hợp lệ! @Edward Shtern: bạn có thể xác nhận rằng bạn chắc chắn đang sử dụng dạng 2-arg của phương thức log4j không? Tôi biết bạn đã đề cập trong một câu trả lời sâu hơn rằng đó là chính sách của công ty để làm như vậy, nhưng bạn có chắc chắn rằng trong trường hợp này bạn đang tuân theo chính sách này không?
KarstenF

Nó có thể là một cú sút xa, nhưng có thể là ngoại lệ bắt nguồn từ một số mã bên thứ 3? Có thể đó là một trình bao bọc ngoại lệ (được viết kém), mà toString () chỉ đơn giản trả về tên lớp của ngoại lệ được bao bọc và không cung cấp dấu vết ngăn xếp bên dưới. Hãy thử đặt một cái gì đó như logger.info ("Exception class =" + exc. Class.getCanonicalName ()) vào khối bắt của bạn và xem những gì bạn nhận được.
KarstenF

4

Đề xuất thay thế - nếu bạn đang sử dụng Eclipse, bạn có thể đặt điểm dừng trên chính NullPulumException (trong phối cảnh Gỡ lỗi, đi tới tab "Điểm dừng" và nhấp vào biểu tượng nhỏ có! Trong đó)

Kiểm tra cả hai tùy chọn "bắt" và "chưa bắt" - bây giờ khi bạn kích hoạt NPE, bạn sẽ ngay lập tức dừng lại và sau đó bạn có thể bước qua và xem chính xác nó được xử lý như thế nào và tại sao bạn không nhận được dấu vết ngăn xếp.


1

toString()chỉ trả về tên ngoại lệ và tin nhắn tùy chọn. Tôi sẽ đề nghị gọi

exception.printStackTrace()

để kết xuất tin nhắn hoặc nếu bạn cần các thông tin chi tiết:

 StackTraceElement[] trace = exception.getStackTrace()

Xem ở trên - Tôi sai chính tả - Tôi đang sử dụng printStackTrace ().
Edward Shtern

1

(Câu hỏi của bạn vẫn chưa rõ về việc mã của bạn có đang gọi không printStackTrace() hay điều này đang được thực hiện bởi một trình xử lý đăng nhập.)

Dưới đây là một số giải thích có thể về những gì có thể xảy ra:

  • Trình ghi / trình xử lý đang được sử dụng đã được cấu hình để chỉ xuất chuỗi thông báo của ngoại lệ, không phải là dấu vết ngăn xếp đầy đủ.

  • Ứng dụng của bạn (hoặc một số thư viện của bên thứ ba) đang ghi nhật ký ngoại lệ bằng cách sử dụng LOG.error(ex);chứ không phải dạng 2 đối số của (ví dụ) phương thức Log4j Logger.

  • Thông điệp đến từ một nơi khác với nơi bạn nghĩ là nó; ví dụ: nó thực sự đến một số phương thức thư viện của bên thứ ba hoặc một số nội dung ngẫu nhiên còn sót lại từ các lần thử trước đó để gỡ lỗi.

  • Ngoại lệ đang được ghi lại đã quá tải một số phương thức để che khuất stacktrace. Nếu đó là trường hợp, ngoại lệ sẽ không phải là NullPulumException chính hãng, nhưng sẽ là một kiểu con tùy chỉnh của NPE hoặc thậm chí một số ngoại lệ không được kết nối.

Tôi nghĩ rằng lời giải thích cuối cùng có thể là khá khó xảy ra, nhưng mọi người ít nhất cũng dự tính làm điều này để "ngăn chặn" kỹ thuật đảo ngược. Tất nhiên nó chỉ thực sự thành công trong việc gây khó khăn cho cuộc sống của các nhà phát triển trung thực.


1

Khi bạn đang sử dụng AspectJ trong dự án của mình, có thể xảy ra việc một số khía cạnh che giấu phần của dấu vết ngăn xếp. Ví dụ, hôm nay tôi đã có:

java.lang.NullPointerException:
  at com.company.product.MyTest.test(MyTest.java:37)

Dấu vết ngăn xếp này đã được in khi chạy thử nghiệm thông qua chắc chắn của Maven.

Mặt khác, khi chạy thử nghiệm trong IntelliJ, một dấu vết ngăn xếp khác đã được in:

java.lang.NullPointerException
  at com.company.product.library.ArgumentChecker.nonNull(ArgumentChecker.java:67)
  at ...
  at com.company.product.aspects.CheckArgumentsAspect.wrap(CheckArgumentsAspect.java:82)
  at ...
  at com.company.product.MyTest.test(MyTest.java:37)

0

Điều này sẽ xuất ngoại lệ, chỉ sử dụng để gỡ lỗi bạn nên xử lý ngoại lệ của bạn tốt hơn.

import java.io.PrintWriter;
import java.io.StringWriter;
    public static String getStackTrace(Throwable t)
    {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        t.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }
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.