RSS

Uważajcie na Class#getSimpleName

Liczba odsłon: 336

W przy­pad­kach, gdy apli­kac­ja na­pi­sa­na w ję­zy­ku Java ma za­pre­zen­to­wać in­for­mac­je diag­no­stycz­ne za­wie­ra­ją­ce naz­wę kla­sy, za­zwy­czaj uży­wa się naz­wy skró­co­nej, zwra­ca­nej przez me­to­dę get­Simple­Name() kla­sy Class. Warto jed­nak pa­mię­tać, że ko­rzy­sta­nie z tej me­to­dy ma bar­dzo ne­ga­tyw­ny wpływ na wy­daj­ność.

Zacznijmy od uru­cho­mie­nia krót­kie­go te­stu:

String name;
long start, stop;
start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; ++i) {
    name = Test1.class.getName();
}
stop = System.currentTimeMillis();
System.out.println("      getName: " + (stop - start) / 10 + " us");
start = System.currentTimeMillis();
for (int i = 0; i < 10_000_000; ++i) {
    name = Test1.class.getSimpleName();
}
stop = System.currentTimeMillis();
System.out.println("getSimpleName: " + (stop - start) / 10 + " us");

Wyniki są bar­dzo wy­mow­ne:

      getName: 2 us
getSimpleName: 122 us

Skąd ta­ka róż­ni­ca? Wystar­czy spoj­rzeć na po­stać źród­ło­wą oby­dwu me­tod:

public String getName() {
    if (name == null)
        name = getName0();
    return name;
}
public String getSimpleName() {
    if (isArray())
        return getComponentType().getSimpleName() + "[]";
    String simpleName = getSimpleBinaryName();
    if (simpleName == null) {
        simpleName = getName();
        return simpleName.substring(simpleName.lastIndexOf(".") + 1);
    }
    int length = simpleName.length();
    if (length < 1 || simpleName.charAt(0) != '$')
        throw new InternalError("Malformed class name");
    int index = 1;
    while (index < length && isAsciiDigit(simpleName.charAt(index)))
        index++;
    return simpleName.substring(index);
}

Podstawo­wa naz­wa kla­sy, utrzy­my­wa­na w we­wnętrz­nych struk­tu­rach da­nych ma­szy­ny wir­tu­al­nej Java, jest po­bie­ra­na jed­no­krot­nie i prze­cho­wy­wa­na w po­lu obiek­tu Class. Koszt od­wo­ła­nia się do ko­du C++ z po­zio­mu pro­gra­mu Java jest na ty­le du­ży, że do­dat­ko­we ob­cią­że­nie pa­mię­ci obiek­tem String prze­sta­je mieć zna­cze­nie. Gdy jed­nak peł­na naz­wa kla­sy jest już do­stęp­na z po­zio­mu ko­du Java, moż­na wy­ko­ny­wać na niej do­wol­ne ope­rac­je, oszczę­dza­jąc pa­mięć kosz­tem mo­cy obli­cze­nio­wej CPU. Stąd bie­rze się właś­nie wi­dzia­na w te­ście róż­ni­ca cza­su reali­zac­ji oby­dwu me­tod.

Ten krót­ki test nie ozna­cza, że nie na­le­ży w ogó­le uży­wać me­to­dy get­Simple­Name(). To, czy po­bra­nie naz­wy kla­sy zaj­mu­je 1 μs czy 100 μs nie ma więk­sze­go zna­cze­nia, gdy chce­my raz na kil­ka go­dzin wy­świet­lić naz­wę kla­sy na ekra­nie lub umieś­cić ją w dzien­ni­ku zda­rzeń. Warto jed­nak pa­mię­tać o tej róż­ni­cy, gdy ma­my za­miar prze­te­sto­wać szyb­kość dzia­ła­nia me­cha­niz­mów wy­ko­rzy­stu­ją­cych we­wnętrz­nie me­to­dę get­Simple­Name(), gdyż mo­że­my stra­cić mnóst­wo cza­su pró­bu­jąc zopty­ma­li­zo­wać coś, co dzia­ła wol­no z po­wo­du nie­win­ne­go na pierw­szy rzut oka po­bra­nia naz­wy kla­sy.