2013-01-09 11 views
11

Występują pewne dziwne problemy z wydajnością JVM.Dlaczego java.lang.Object.getClass() (i odbicie) jest wolniejszy niż zwykle?

Mamy duży i nieco nieprzejrzysty składnik GUI (Uruchom arkusz kalkulacyjny Formuły 1).

Jeśli zainicjujemy całość z wątku wywołania zdarzenia (tak, jak powinno), stwierdzimy, że kod działa znacznie wolniej (przeciągając mysz, aby wybrać komórki, zauważalne jest opóźnienie).

Jeśli zainicjujemy go po raz pierwszy w głównym wątku programu uruchamiającego, a dopiero potem zaczniemy go używać w EDT, działa znacznie szybciej.

Kiedy patrzę dlaczego wykonuje się powoli za pomocą profilera, wywołania metody, które biorą cały czas są:

  • java.lang.Object.getClass()
  • java.lang .reflect.Array.newInstance (klasa, int)
  • java.lang.Class.getComponentType()
  • java.lang.Thread.currentThread()

Jesteśmy przy użyciu 64-bitowego JVM Sun Hotspot w systemie Windows 7 (ten, który jest dostarczany z JDK).

Czy ktokolwiek wie z jakiegokolwiek powodu, dlaczego powyższe metody mogą działać drastycznie wolniej niż zwykle?

Myślę, że może ma to coś wspólnego z porządkiem, w którym klasy są ładowane ... czy to rozsądna teoria? Czy ktoś wie o innych sposobach, w których mogę zdiagnozować, dlaczego wywoływanie tych metod może zająć dużo czasu?

Dołączyłem dwa zrzuty ekranu z profilera. W obu przypadkach wszystko, co robiłem, to przeciąganie myszą wokół komórek arkusza kalkulacyjnego podczas działania profilera. Jest to po prostu aktualizacja komponentu GUI i nie robi się nic więcej.

Pierwszy zajmuje dużo czasu w metodzie zwanej "releaseLock()". Z jakiegoś powodu zajmuje to dużo czasu, ponieważ "getComponentType()" trwa znacznie dłużej niż zwykle.

releaseLock

Drugi jest po zrobiłem „hack”, aby usunąć koszt „releaseLock()” - ale teraz to po prostu spędza dużo czasu w „getLock()” z powodu getClass() i currentThread() przy znacznie dłużej niż normalnie:

getLock

Ale ważne jest to, że jeśli po prostu zmienić kolejność, w której kod jest zainicjowany, nic z tego kodu trwa bardzo długo, aby wykonać (nie robi nawet w ogóle nie pojawiają się w profilu).

Jeśli miałem zoptymalizować funkcję getLock(), aplikacja nadal działa znacznie wolniej. Problem polega na tym, że metody takie jak getClass() trwają zbyt długo. Nie ma sposobu, aby to zrekompensować - getClass() zostaje wywołany w zbyt wielu miejscach!

Różnica w wydajności jest zauważalna nawet bez pracy profilera.

Należy pamiętać, że nie możemy zmienić żadnego z tych kodów - jest to składnik zewnętrzny. Wyzwaniem jest wyjaśnienie, dlaczego kod działa o wiele wolniej w pewnych okolicznościach w porównaniu z innymi.

+1

Czy możesz dodać zrzut ekranu profilowania? Ponadto profilowanie może mieć znaczący wpływ na środowisko wykonawcze. Czy możesz wykluczyć te metody z profilowania i sprawdzić, czy istnieją inne powody opóźnienia? – jlordo

+0

Nie wiem, jak wykluczyć te metody z profilowania, ale jak widać, korzysta się z próbkowania i pokazując podział. Myślę, że to, co pokazuje profiler, jest poprawne - nie jest tak, że wykonuje inny kod, po prostu z jakiegoś powodu te wywołania metod, które są zwykle dość niedrogie (Object.getClass()), teraz trwają długo. –

+1

Czy ten wynik jest spójny? Ponieważ jeśli zdarza się to tylko raz, może to być problem z próbkowaniem ("pech" przy wyborze próbki). –

Odpowiedz

1

Możesz spróbować wykonać kilka mieszanych java + natywnych zrzutów wątku aplikacji pod obciążeniem.

Prawdopodobnie dałoby to pewne wskazówki na temat tego, co dzieje się pod maską Object.getClass().

Zobacz także If profiler is not the answer, what other choices do we have?.

Zobacz także What does thread dump looks like when JVM spent time in GC. Przyczyna Zwykłe zrzuty JVM mogą nie zawsze być idealnie dokładne, ponieważ zdarzają się tylko na safepoints.

Powiązane problemy