Aby uzyskać rozsądną implementację języka Java:
Każdy obiekt ma nagłówek zawierający między innymi wskaźnik do typu środowiska wykonawczego (na przykład Double
lub String
, ale nigdy nie może to być CharSequence
lub AbstractList
). Zakładając, że kompilator środowiska uruchomieniowego (ogólnie HotSpot w przypadku Sun'a) nie może statycznie określić typu, a wygenerowany kod maszynowy musi przeprowadzić pewne sprawdzenia.
Najpierw należy odczytać wskaźnik do typu środowiska wykonawczego. Jest to i tak konieczne do wywołania metody wirtualnej w podobnej sytuacji.
W przypadku rzutowania na typ klasy wiadomo dokładnie, ile jest superklas, dopóki nie trafisz java.lang.Object
, więc typ można odczytać ze stałym przesunięciem od wskaźnika typu (właściwie pierwsza osiem w HotSpot). Ponownie jest to analogiczne do odczytu wskaźnika metody dla metody wirtualnej.
Wtedy odczytana wartość wymaga porównania z oczekiwanym statycznym typem rzutowania. W zależności od architektury zestawu instrukcji, inna instrukcja będzie musiała rozgałęzić się (lub spowodować błąd) w nieprawidłowej gałęzi. ISA, takie jak 32-bitowe ARM, mają instrukcje warunkowe i mogą mieć możliwość przejścia smutnej ścieżki przez szczęśliwą ścieżkę.
Interfejsy są trudniejsze z powodu wielokrotnego dziedziczenia interfejsu. Zwykle ostatnie dwa rzuty do interfejsów są buforowane w typie środowiska uruchomieniowego. W bardzo wczesnych dniach (ponad dziesięć lat temu) interfejsy były nieco powolne, ale to już nie ma znaczenia.
Miejmy nadzieję, że widzisz, że tego rodzaju rzeczy są w dużej mierze nieistotne dla wydajności. Twój kod źródłowy jest ważniejszy. Jeśli chodzi o wydajność, największym hitem w twoim scenariuszu mogą być chybienia w pamięci podręcznej spowodowane ściganiem wskaźników obiektów w całym miejscu (informacje o typie będą oczywiście powszechne).