2012-10-30 18 views
5

Nếu bạn nhận được kết xuất ngăn xếp của một quá trình, ví dụ: thông qua jstack, bạn sẽ nhận được thông tin về các màn hình bị khóa (và các bộ đồng bộ hóa) với một địa chỉ cho mỗi quá trình. Ví dụ: từ một trivially bế tắc quá trình gồm hai chủ đề (sử dụng jstack):Cách có lập trình để lấy địa chỉ được hiển thị cho các màn hình Java trong ngăn xếp ngăn xếp?

"Thread-0" prio=10 tid=0x00007f1444042000 nid=0x2818 waiting for monitor entry [0x00007f14433ca000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
    at scrap.DeadlockTest$Deadlocker.run(DeadlockTest.java:49) 
    - waiting to lock <0x00000007c14e6378> (a java.lang.Object) 
    - locked <0x00000007c14e6368> (a java.lang.Object) 
    at java.lang.Thread.run(Thread.java:619) 

... (omitted some lines here) 

Java stack information for the threads listed above: 
=================================================== 
"Thread-1": 
    at scrap.DeadlockTest$Deadlocker.run(DeadlockTest.java:49) 
    - waiting to lock <0x00000007c14e6368> (a java.lang.Object) 
    - locked <0x00000007c14e6378> (a java.lang.Object) 
    at java.lang.Thread.run(Thread.java:619) 
"Thread-0": 
    at scrap.DeadlockTest$Deadlocker.run(DeadlockTest.java:49) 
    - waiting to lock <0x00000007c14e6378> (a java.lang.Object) 
    - locked <0x00000007c14e6368> (a java.lang.Object) 
    at java.lang.Thread.run(Thread.java:619) 

Có cách nào để có được, khi chạy trong mã Java, các địa chỉ tương tự hiển thị ở trên, chẳng hạn như 0x00000007c14e6368?

Tôi đã thử sử dụng mã băm nhận dạng trên các đối tượng màn hình tương ứng, cũng như MonitorInfo qua ThreadMXBean, không có may mắn (giá trị không tương ứng, ít nhất trên java 64 bit).

Trả lời

-2

Vì vậy, câu hỏi của bạn là thực sự, có cách nào để xác định hai đối tượng nào bị bế tắc trong một chương trình không? Ví dụ tầm thường này thực sự chỉ ra một cách để kiểm tra bế tắc. Một trong những nguyên nhân phổ biến nhất của bế tắc là lấy nhiều khóa, nhưng theo hai thứ tự khác nhau. Vì vậy, tìm kiếm mã nơi nó khóa để đảm bảo bạn luôn yêu cầu các khóa theo cùng thứ tự.

Ngoài ra, thực tế là bạn đang yêu cầu nhiều khóa để thực hiện một số thao tác có nghĩa là bạn nên thay đổi thiết kế để chỉ yêu cầu một khóa duy nhất cho bất kỳ thao tác nào.

+0

Không, đó không phải là câu hỏi của tôi. Câu hỏi đặt ra là tôi có một quá trình mà deadlocks nói cứ 100.000 lần lặp lại trong sản xuất, và tôi muốn tìm ra các đối tượng thực sự tham gia vào bế tắc, có lẽ bằng cách ghi lại các địa chỉ màn hình tại một số điểm. Thay đổi thiết kế để chỉ yêu cầu một khóa duy nhất cho bất kỳ hoạt động nào? lol. – BeeOnRope

+0

Không phải là một khóa duy nhất cho tất cả các hoạt động, nhưng một khóa duy nhất cho bất kỳ hoạt động đơn lẻ nào. Tôi nghĩ rằng một đánh giá mã thực sự sẽ tìm thấy vấn đề. Điều này nghe có vẻ chính xác như những gì tôi vừa mô tả. – Zagrev

+0

-1 vì không đọc câu hỏi của các bạn! – Toby

3

Tôi cho rằng, không có cách nào dễ dàng để lấy địa chỉ của màn hình. Đây là cách jstack hiện nó

import com.sun.tools.attach.VirtualMachine; 
import sun.tools.attach.HotSpotVirtualMachine; 

import java.io.InputStream; 
import java.lang.management.ManagementFactory; 

public class Main { 

    public static void main(String[] args) throws Exception { 
     VirtualMachine vm = VirtualMachine.attach(getPid()); 

     HotSpotVirtualMachine hsvm = (HotSpotVirtualMachine) vm; 
     InputStream in = hsvm.remoteDataDump("-l"); 

     byte b[] = new byte[256]; 
     int n; 
     do { 
      n = in.read(b); 
      if (n > 0) { 
       String s = new String(b, 0, n, "UTF-8"); 
       System.out.print(s); 
      } 
     } while (n > 0); 
     in.close(); 
    } 

    private static String getPid() { 
     String name = ManagementFactory.getRuntimeMXBean().getName(); 
     int ind = name.indexOf('@'); 
     return name.substring(0, ind); 
    } 

} 

Để chạy đoạn mã này đừng quên thêm $JDK_HOME/lib/tools.jar vào đường dẫn lớp.

Đây là kết quả nó tạo 2012-10-31 08:48:08 Full chủ đề bãi Java HotSpot (TM) 64-Bit Server VM (20,5-b03 chế độ hỗn hợp):

"Monitor Ctrl-Break" daemon prio=6 tid=0x0000000006b98000 nid=0x1d70 runnable [0x00000000074df000] 
    java.lang.Thread.State: RUNNABLE 
    at java.net.PlainSocketImpl.socketAccept(Native Method) 
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408) 
    - locked <0x00000007d5d53148> (a java.net.SocksSocketImpl) 
    at java.net.ServerSocket.implAccept(ServerSocket.java:462) 
    at java.net.ServerSocket.accept(ServerSocket.java:430) 
    at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:82) 
    at java.lang.Thread.run(Thread.java:662) 

    Locked ownable synchronizers: 
    - None 

... 

Chúng ta hãy xem xét kỹ hơn vào những gì hsvm.remoteDataDump("-l") không

... 

public InputStream remoteDataDump(Object ... args) throws IOException { 
    return executeCommand("threaddump", args); 
} 

/* 
* Execute the given command in the target VM - specific platform 
* implementation must implement this. 
*/ 
abstract InputStream execute(String cmd, Object ... args) 
    throws AgentLoadException, IOException; 

/* 
* Convenience method for simple commands 
*/ 
private InputStream executeCommand(String cmd, Object ... args) throws IOException { 
    try { 
     return execute(cmd, args); 
    } catch (AgentLoadException x) { 
     throw new InternalError("Should not get here"); 
    } 
} 
... 

và đây là một thực hiện các phương thức execute cho các cửa sổ (bạn có thể tìm thấy nó trong sun.tools.attach.WindowsVirtualMachine)

InputStream execute(String cmd, Object ... args) 
     throws AgentLoadException, IOException { 

    assert args.length <= 3;  // includes null 

    // create a pipe using a random name 
    int r = (new Random()).nextInt(); 
    String pipename = "\\\\.\\pipe\\javatool" + r; 
    long hPipe = createPipe(pipename); 

    // check if we are detached - in theory it's possible that detach is invoked 
    // after this check but before we enqueue the command. 
    if (hProcess == -1) { 
     closePipe(hPipe); 
     throw new IOException("Detached from target VM"); 
    } 

    try { 
     // enqueue the command to the process 
     enqueue(hProcess, stub, cmd, pipename, args); 

     // wait for command to complete - process will connect with the 
     // completion status 
     connectPipe(hPipe); 

     // create an input stream for the pipe 
     PipedInputStream is = new PipedInputStream(hPipe); 

     // read completion status 
     int status = readInt(is); 
     if (status != 0) { 
      // special case the load command so that the right exception is thrown 
      if (cmd.equals("load")) { 
       throw new AgentLoadException("Failed to load agent library"); 
      } else { 
       throw new IOException("Command failed in target VM"); 
      } 
     }  

     // return the input stream 
     return is; 

    } catch (IOException ioe) { 
     closePipe(hPipe); 
     throw ioe; 
    } 
} 

static native void init(); 

static native byte[] generateStub(); 

static native long openProcess(int pid) throws IOException; 

static native void closeProcess(long hProcess) throws IOException; 

static native long createPipe(String name) throws IOException; 

static native void closePipe(long hPipe) throws IOException; 

static native void connectPipe(long hPipe) throws IOException;  

static native int readPipe(long hPipe, byte buf[], int off, int buflen) throws IOException; 

static native void enqueue(long hProcess, byte[] stub, 
    String cmd, String pipename, Object ... args) throws IOException; 

Vì vậy, về cơ bản các đường ống đặt tên được mở ra và một số lệnh được thực thi trên nó và tất cả sự kỳ diệu là trong mã nguồn gốc trong hotspot/src/share/vm/services/attachListener.cpp

// Implementation of "threaddump" command - essentially a remote ctrl-break 
// 
static jint thread_dump(AttachOperation* op, outputStream* out) { 
    bool print_concurrent_locks = false; 
    if (op->arg(0) != NULL && strcmp(op->arg(0), "-l") == 0) { 
     print_concurrent_locks = true; 
    } 

    // thread stacks 
    VM_PrintThreads op1(out, print_concurrent_locks); 
    VMThread::execute(&op1); 

    // JNI global handles 
    VM_PrintJNI op2(out); 
    VMThread::execute(&op2); 

    // Deadlock detection 
    VM_FindDeadlocks op3(out); 
    VMThread::execute(&op3); 

    return JNI_OK; 
} 

Nói chung nếu bạn muốn trích xuất các địa chỉ của đối tượng bạn đã có được màn hình, bạn có thể phân tích đầu ra của đoạn đầu tiên và trích xuất các đoạn cần thiết, ví dụ, theo id luồng.

Các tùy chọn khác được đính kèm vào quy trình của bạn ở chế độ gỡ lỗi và sử dụng trình gỡ lỗi api hoặc JNI.

0

Có lẽ bạn có thể ghi lại đối tượng yêu cầu khóa. Sau đó, bạn sẽ biết ai có khóa tại thời điểm bế tắc.