Làm thế nào để phân tích một kết xuất luồng java?


100

Tôi đang cố gắng hiểu thêm về java, đặc biệt là về quản lý bộ nhớ và luồng. Vì lý do này, tôi gần đây đã tìm thấy hứng thú khi xem xét các bãi chứa chỉ.

Dưới đây là một vài dòng được lấy từ một ứng dụng web bằng VisualVM, một công cụ tích hợp sẵn cho java:

"Finalizer" daemon prio=8 tid=0x02b3d000 nid=0x898 in Object.wait() [0x02d0f000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x27ef0288> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:118)
    - locked <0x27ef0288> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:134)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)

   Locked ownable synchronizers:
    - None

"Reference Handler" daemon prio=10 tid=0x02b3b800 nid=0x494 in Object.wait() [0x02cbf000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x27ef0310> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:485)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
    - locked <0x27ef0310> (a java.lang.ref.Reference$Lock)

Đầu tiên, tôi có câu hỏi về một số tên biến:

  • tid và nid nghĩa là gì?
  • Hình trong ngoặc vuông sau Object.wait là gì?

Sau đó, đối với bản thân dấu vết ngăn xếp:

  • nó có nghĩa là gì khi đợi trên <.....> (java.lang ....) và số trong <..>
  • Nó có nghĩa là gì bị khóa <.....> (một java.lang ....) cùng một câu hỏi, có gì trong <..>

Tôi nghĩ rằng từ khóa theo cách nào đó có liên quan đến điều kiện chờ, tuy nhiên, tôi đã nhầm. Trên thực tế, tôi tự hỏi tại sao bị khóa lặp lại ba lần, nhưng luồng ở trạng thái có thể chạy được như được thấy trong cùng một kết xuất:

"Thread-0" prio=6 tid=0x02ee3800 nid=0xc1c runnable [0x03eaf000]
   java.lang.Thread.State: RUNNABLE
    at java.io.FileInputStream.readBytes(Native Method)
    at java.io.FileInputStream.read(FileInputStream.java:199)
    at java.io.BufferedInputStream.read1(BufferedInputStream.java:256)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:317)
    - locked <0x23963378> (a java.io.BufferedInputStream)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
    - locked <0x23968450> (a java.io.InputStreamReader)
    at java.io.InputStreamReader.read(InputStreamReader.java:167)
    at java.io.BufferedReader.fill(BufferedReader.java:136)
    at java.io.BufferedReader.readLine(BufferedReader.java:299)
    - locked <0x23968450> (a java.io.InputStreamReader)
    at java.io.BufferedReader.readLine(BufferedReader.java:362)
    at org.codehaus.plexus.util.cli.StreamPumper.run(StreamPumper.java:145)

Cuối cùng, đây là điều tồi tệ nhất trong số họ:

"CompilerThread0" daemon prio=10 tid=0x02b81000 nid=0x698 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

Chủ đề này ở trạng thái chạy được, nhưng nó đang chờ trong điều kiện. Điều kiện gì và 0x00000 là gì?

Tại sao dấu vết ngăn xếp quá ngắn mà không có bất kỳ bằng chứng nào về lớp luồng?

Nếu bạn có thể trả lời tất cả các câu hỏi của tôi, tôi sẽ rất biết ơn.

Cảm ơn

Câu trả lời:


113

TID là idad và NID là: Native thread ID. ID này phụ thuộc nhiều vào nền tảng. Đó là NID trong kết xuất chuỗi jstack. Trên Windows, nó chỉ đơn giản là ID luồng cấp hệ điều hành trong một quy trình. Trên Linux và Solaris, đó là PID của luồng (đến lượt nó là một quy trình nhẹ). Trên Mac OS X, nó được cho là giá trị pthread_t gốc.

Truy cập liên kết này: ID luồng cấp Java : để biết định nghĩa và giải thích thêm về hai thuật ngữ này.

Trên trang của IBM, tôi tìm thấy liên kết này: Cách diễn giải kết xuất luồng . bao gồm điều này chi tiết hơn:

Nó giải thích việc chờ đợi có nghĩa là gì: Một khóa ngăn nhiều thực thể truy cập vào tài nguyên được chia sẻ. Mỗi đối tượng trong Java ™ có một khóa liên quan (có được bằng cách sử dụng một khối hoặc phương pháp được đồng bộ hóa). Trong trường hợp của JVM, các luồng cạnh tranh cho các tài nguyên khác nhau trong JVM và khóa trên các đối tượng Java.

Sau đó, nó mô tả màn hình như một loại cơ chế khóa đặc biệt được sử dụng trong JVM để cho phép đồng bộ hóa linh hoạt giữa các luồng. Đối với mục đích của phần này, hãy đọc các điều khoản theo dõi và khóa thay thế cho nhau.

Sau đó, nó đi xa hơn:

Để tránh có một màn hình trên mọi đối tượng, JVM thường sử dụng một cờ trong một lớp hoặc khối phương thức để chỉ ra rằng mục đó đã bị khóa. Hầu hết thời gian, một đoạn mã sẽ chuyển một số đoạn bị khóa mà không gây tranh cãi. Do đó, lá cờ hộ mệnh là đủ để bảo vệ đoạn mã này. Đây được gọi là màn hình phẳng. Tuy nhiên, nếu một chủ đề khác muốn truy cập vào một số mã bị khóa, thì một cuộc tranh cãi thực sự đã xảy ra. JVM bây giờ phải tạo (hoặc thổi phồng) đối tượng giám sát để giữ luồng thứ hai và sắp xếp cho một cơ chế báo hiệu để điều phối quyền truy cập vào phần mã. Màn hình này hiện được gọi là màn hình thổi phồng.

Đây là lời giải thích chuyên sâu hơn về những gì bạn đang thấy trên các dòng từ kết xuất chuỗi. Một luồng Java được thực hiện bởi một luồng riêng của hệ điều hành. Mỗi chủ đề được thể hiện bằng một dòng in đậm như:

"Luồng-1" (TID: 0x9017A0, sys_thread_t: 0x23EAC8, state: R, native ID: 0x6E4) prio = 5

* 6 mục sau giải thích điều này vì tôi đã đối sánh chúng từ ví dụ, các giá trị trong dấu ngoặc []:

  1. tên [ Chủ đề-1 ],
  2. số nhận dạng [ 0x9017A0 ],
  3. Địa chỉ cấu trúc dữ liệu JVM [ 0x23EAC8 ],
  4. trạng thái hiện tại [ R ],
  5. mã định danh chuỗi gốc [ 0x6E4 ],
  6. và ưu tiên [ 5 ].

"Chờ trên" dường như là một luồng daemon được liên kết với chính jvm chứ không phải luồng ứng dụng. Khi bạn nhận được "trong Object.wait ()", điều đó có nghĩa là chuỗi daemon, "finalizer" ở đây, đang đợi thông báo về khóa trên một đối tượng, trong trường hợp này, nó cho bạn biết nó đang chờ thông báo nào: "- đang đợi trên <0x27ef0288> (java.lang.ref.ReferenceQueue $ Lock) "

Định nghĩa của ReferenceQueue là: Hàng đợi tham chiếu, mà các đối tượng tham chiếu đã đăng ký được thêm vào bởi bộ thu gom rác sau khi các thay đổi khả năng truy cập thích hợp được phát hiện.

Luồng trình hoàn thiện chạy để bộ sưu tập rác hoạt động để dọn dẹp các tài nguyên liên kết với một đối tượng. Nếu tôi nhìn thấy nó một cách chính xác, trình hoàn thiện không thể lấy khóa đối tượng này: java.lang.ref.ReferenceQueue.remove (ReferenceQueue.java:118) bởi vì đối tượng java đang chạy một phương thức, vì vậy chuỗi trình hoàn thiện là bị khóa cho đến khi đối tượng đó hoàn thành với tác vụ hiện tại của nó.

Ngoài ra, trình hoàn thiện không chỉ tìm cách lấy lại bộ nhớ mà còn liên quan nhiều hơn đến việc dọn dẹp tài nguyên. Tôi cần nghiên cứu thêm về nó, nhưng nếu bạn có các tệp đang mở, ổ cắm, v.v. liên quan đến phương thức đối tượng, thì trình hoàn thiện cũng sẽ giải phóng các mục đó.

Hình trong ngoặc vuông sau Object.wait trong chuỗi kết xuất là gì?

Nó là một con trỏ trong bộ nhớ tới luồng. Đây là mô tả chi tiết hơn:

C.4.1 Thông tin chủ đề

Phần đầu tiên của phần luồng hiển thị luồng đã gây ra lỗi nghiêm trọng, như sau:

Current thread (0x0805ac88):  JavaThread "main" [_thread_in_native, id=21139]
                    |             |         |            |          +-- ID
                    |             |         |            +------------- state
                    |             |         +-------------------------- name
                    |             +------------------------------------ type
                    +-------------------------------------------------- pointer

Con trỏ luồng là con trỏ đến cấu trúc luồng nội bộ của máy ảo Java. Nó thường không được quan tâm trừ khi bạn đang gỡ lỗi một máy ảo Java trực tiếp hoặc tệp lõi.

Mô tả cuối cùng này đến từ: Hướng dẫn gỡ rối cho Java SE 6 với HotSpot VM

Dưới đây là một số liên kết khác về kết xuất chuỗi:


11

Thêm vào câu trả lời tuyệt vời của @James Drinkard:

Lưu ý rằng, tùy thuộc vào việc triển khai bên dưới, trạng thái java.lang.Thread.State của một chuỗi bị chặn trong một phương thức gốc có thể được báo cáo là RUNNABLE, ở đâuA thread in the runnable state is executing in the Java virtual machine but it may be waiting for other resources from the operating system such as processor.

Nó chỉ ra rằng mô tả này cũng bao gồm việc bị chặn trong một cuộc gọi OS như thăm dò ý kiến ​​hoặc thao tác đọc - có lẽ vì không có gì đảm bảo rằng JVM có thể biết khi nào một cuộc gọi phương thức gốc đã bị chặn ở cấp hệ điều hành.

Nhiều cuộc thảo luận về kết xuất chuỗi JVM mà tôi đã thấy hoặc bỏ qua hoàn toàn khả năng này hoặc lướt qua nó mà không xem xét các tác động - không ít trong số đó là các công cụ giám sát có thể báo cáo một cách khó hiểu rằng một số chuỗi như vậy đang 'chạy', và hơn nữa tất cả chúng đều đang chạy ở mức 100%.

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.