2016-05-18 14 views
26

Meine Anwendung verwendet Gson 2.2 zum Konvertieren von POJOs zu JSON. Als ich einen Belastungstest machen stieß ich auf eine Menge in Gson Konstruktor blockierten Threads:Wie finde ich heraus, welcher Thread den Monitor hält?

"http-apr-28201-exec-28" #370 daemon prio=5 os_prio=0 tid=0x0000000001ee7800 nid=0x62cb waiting for monitor entry [0x00007fe64df9a000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
    at com.google.gson.Gson.<init>(Gson.java:200) 
    at com.google.gson.Gson.<init>(Gson.java:179) 

Thread-Dump nicht alle Threads nicht zeigen [0x00007fe64df9a000] monitor hält. Wie kann ich herausfinden, wer es hält?

Gson Code at line 200 sieht ziemlich unschuldig:

// built-in type adapters that cannot be overridden 
factories.add(TypeAdapters.STRING_FACTORY); 
factories.add(TypeAdapters.INTEGER_FACTORY); 

Ich verwende JRE 1.8.0_91 auf Linux

+1

haben Sie versucht [Eclipse Matte] (http://www.eclipse.org/mat/)? – fhofmann

+0

@fhofmann Ich habe keine Probleme mit Heap - wie kann es mir helfen? – bedrin

+0

Sie haben Recht. Ich hatte vor einiger Zeit ein ähnliches Problem und habe Eclipse Mat, einen Profiler (yourkit) und einen speziellen Parser verwendet, um festzustellen, dass das Problem eine HashTable (Eigenschaften) war, die von der API-Konfiguration verwendet wurde. MAT hat mir geholfen, relevante Klassen zu finden. – fhofmann

Antwort

12

tl; dr Ich glaube, Sie laufen in GC-ähnliches Verhalten, wo Fäden sein werden In Wartestatus versetzen, um die Speicherbereinigung zu ermöglichen.


Ich habe nicht die ganze Wahrheit, aber ich hoffe, einige Einblicke zu liefern.

Zuerst Sache zu realisieren ist, dass die Zahl in Klammern, [0x00007fe64df9a000], ist nicht die Adresse eines Monitors. Die Zahl in Klammern kann für alle Threads in einem Speicherauszug angezeigt werden, auch für Threads, die sich im Status "Running" befinden. Die Nummer ändert sich auch nicht. Beispiel aus meinem Test-Dump:

main" #1 prio=5 os_prio=0 tid=0x00007fe27c009000 nid=0x27e5c runnable [0x00007fe283bc2000] 
    java.lang.Thread.State: RUNNABLE 
     at Foo.main(Foo.java:12) 

Ich bin nicht sicher, was die Zahl bedeutet, aber this page Hinweise darauf, dass es ist:

... der Zeiger auf die Java VM Innengewinde Struktur. Es ist im Allgemeinen nicht von Interesse, es sei denn, Sie debuggen eine Live-Java-VM oder Core-Datei.

Obwohl das Format der Spur erklärt dort ein bisschen anders ist, bin ich mir nicht sicher, ob ich richtig bin.

Die Art und Weise eine Müllhalde aussieht, wenn die Adresse des eigentlichen Monitor angezeigt wird:

"qtp48612937-70" #70 prio=5 os_prio=0 tid=0x00007fbb845b4800 nid=0x133c waiting for monitor entry [0x00007fbad69e8000] 
    java.lang.Thread.State: BLOCKED (on object monitor) 
     at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:233) 
     - waiting to lock <0x00000005b8d68e90> (a java.lang.Object) 

Beachten Sie die waiting to lock Linie in der Spur und dass die Adresse des Monitors aus der Zahl in Klammern unterscheidet.

Die Tatsache, dass wir die Adresse des betroffenen Monitors nicht sehen können, zeigt an, dass der Monitor nur in nativem Code existiert.

Zweitens, der betreffende Gson-Code enthält überhaupt keine Synchronisation. Der Code fügt einfach ein Element zu einer ArrayList hinzu (vorausgesetzt, es wurde keine Bytecode-Manipulation vorgenommen und nichts wird auf niedrigem Niveau fischartig gemacht). Das heißt, es wäre nicht sinnvoll, den Thread bei diesem Aufruf auf einen Standard-Synchronisationsmonitor warten zu sehen.

Ich fand some, indications, dass Threads angezeigt werden können, wie auf einen Monitoreintrag warten, wenn es viel GC gibt.

Ich schrieb ein einfaches Testprogramm zu versuchen, es zu reproduzieren, indem nur eine Menge von Elementen zu einer Array-Liste hinzufügen:

List<String> l = new ArrayList<>(); 
while (true) { 
    for (int i = 0; i < 100_100; i++) { 
      l.add("" + i); 
    } 
    l = new ArrayList<>(); 
} 

Dann nahm ich Thread-Dumps dieses Programms. Gelegentlich lief ich in die folgenden Spur:

"main" #1 prio=5 os_prio=0 tid=0x00007f35a8009000 nid=0x12448 waiting on condition [0x00007f35ac335000] 
    java.lang.Thread.State: RUNNABLE 
     at Foo.main(Foo.java:10) <--- Line of l.add() 

Zwar nicht identisch mit der Spur des OP, ist es interessant, einen Thread zu haben waiting on condition wenn keine Synchronisation beteiligt ist. Ich habe es häufiger mit einem kleineren Heap erlebt, was darauf hindeutet, dass es GC-bezogen sein könnte.

Eine andere Möglichkeit könnte sein, dass Code, der die Synchronisation enthält, JIT kompiliert wurde und das verhindert, dass Sie die tatsächliche Adresse des Monitors sehen. Ich denke jedoch, dass das weniger wahrscheinlich ist, da Sie es auf ArrayList.add erfahren. Wenn das der Fall ist, weiß ich keine Möglichkeit, den tatsächlichen Besitzer des Monitors herauszufinden.

+0

Interessant! Ich stimme zu - es sieht so aus, als ob dieses Verhalten von GC verursacht wird. Zumal ich gerade einen Belastungstest mache. Ich werde versuchen, mehr Telemetrie von VM zu bekommen, um es zu beweisen – bedrin

+0

Sie hatten Recht! Ich habe den Belastungstest gegen einen Server mit angehängtem Profiler gestartet und viele große GC-Ereignisse gesehen, die durch eine große Anzahl von Anfragen verursacht wurden (deshalb brauchen wir Belastungstests!). Ich denke, dass, wenn ich die Last erhöht hätte, würde ich mit "GC Overhead Limit überschritten" Ausnahmen enden, aber anscheinend war meine Ladung nicht groß genug – bedrin

Verwandte Themen