Pewnego dnia natknąłem się na pakiet sun.misc.Unsafe i byłem zaskoczony tym, co może zrobić.
Oczywiście klasa nie jest udokumentowana, ale zastanawiałem się, czy kiedykolwiek był dobry powód, aby z niej korzystać. Jakie scenariusze mogą się pojawić, gdzie będziesz musiał z niego skorzystać? Jak można go wykorzystać w scenariuszu w świecie rzeczywistym?
Co więcej, jeśli go potrzebujesz, czy nie oznacza to, że coś jest prawdopodobnie nie tak z twoim projektem?
Dlaczego Java w ogóle obejmuje tę klasę?
Odpowiedzi:
przykłady
„Intrinsification” maszyny wirtualnej. tj. CAS (porównaj i zamień) używany w tablicach skrótu bez blokady, np .: sun.misc.Unsafe.compareAndSwapInt może tworzyć prawdziwe wywołania JNI w natywnym kodzie zawierającym specjalne instrukcje dla CAS
czytaj więcej o CAS tutaj http://en.wikipedia.org/wiki/Compare-and-swap
Funkcji sun.misc.Unsafe maszyny wirtualnej hosta można użyć do przydzielenia niezainicjowanych obiektów, a następnie zinterpretować wywołanie konstruktora jako każde inne wywołanie metody.
Można śledzić dane z adresu natywnego. Możliwe jest odzyskanie adresu pamięci obiektu za pomocą klasy java.lang.Unsafe i operowanie na jego polach bezpośrednio za pomocą niebezpiecznych metod get / put!
Kompiluj optymalizacje czasu dla JVM. Wysoka wydajność maszyny wirtualnej wykorzystującej „magię”, wymagającą operacji na niskim poziomie. np .: http://en.wikipedia.org/wiki/Jikes_RVM
Przydzielanie pamięci, sun.misc.Unsafe.allocateMemory np .: - Konstruktor DirectByteBuffer wywołuje ją wewnętrznie po wywołaniu ByteBuffer.allocateDirect
Śledzenie stosu wywołań i odtwarzanie z wartościami utworzonymi przez sun.misc.Unsafe, przydatne do instrumentacji
sun.misc.Unsafe.arrayBaseOffset i arrayIndexScale można wykorzystać do opracowania tablic, techniki skutecznego dzielenia dużych tablic na mniejsze obiekty w celu ograniczenia kosztów skanowania, aktualizacji lub przenoszenia dużych obiektów w czasie rzeczywistym
http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java
więcej informacji na temat referencji tutaj - http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html
źródło
Po uruchomieniu wyszukiwania w jakiejś wyszukiwarce kodu otrzymuję następujące przykłady:
Istnieje wiele innych przykładów, wystarczy kliknąć powyższy link ...
źródło
Co ciekawe, nigdy nawet nie słyszałem o tej klasie (co jest prawdopodobnie dobrą rzeczą, naprawdę).
Jedną z rzeczy, które przychodzą mi na myśl, jest użycie Niebezpiecznego # setMemory do zerowania buforów zawierających poufne informacje w jednym punkcie (hasła, klucze, ...). Mógłbyś nawet zrobić to z polami „niezmiennych” obiektów (i znów przypuszczam, że zwykłe stare odbicie również może załatwić sprawę). Nie jestem ekspertem od bezpieczeństwa, więc weź to z odrobiną soli.
źródło
I'd never even heard of this class
... Mówiłem ci o tym wiele razy! westchnienie + :(park()
dokumentację: „Zablokuj bieżący wątek, powracając, gdy nastąpi równoważenie unparkowania równoważącego lub już wystąpił równoważenie unparkingu, lub wątek jest przerywany lub, jeśli nie jest absolutny, a czas nie jest równy zero, w określonym czasie minęły nanosekundy, a jeśli są bezwzględne, podany termin w milisekundach od epoki minął lub nieoczekiwanie (tzn. wraca bez żadnego „powodu”) ”. Prawie tak dobre, jak „pamięć jest zwalniana po wyjściu z programu lub, w losowych odstępach, w zależności od tego, co nastąpi wcześniej”.Opierając się na bardzo krótkiej analizie biblioteki Java 1.6.12 wykorzystującej eclipse do śledzenia referencji, wydaje się, że każda przydatna funkcjonalność
Unsafe
jest ujawniona w użyteczny sposób.Operacje CAS są udostępniane za pośrednictwem klas Atomic *. Funkcje manipulowania pamięcią są ujawniane przez instrukcje DirectByteBuffer Sync (parkowanie, unparkowanie) są ujawniane przez AbstractQueuedSynchronizer, który z kolei jest wykorzystywany przez implementacje Lock.
źródło
LockSupport
AQS (ten ostatni jest bardziej domniemany niż parkowanie / unpark)Unsafe.throwException - pozwala na rzucenie sprawdzonego wyjątku bez deklarowania go.
Jest to przydatne w niektórych przypadkach, gdy masz do czynienia z refleksją lub AOP.
Załóżmy, że zbudujesz ogólny serwer proxy dla interfejsu zdefiniowanego przez użytkownika. A użytkownik może określić, który wyjątek jest zgłaszany przez zastosowanie w specjalnym przypadku, po prostu deklarując wyjątek w interfejsie. To jest jedyny sposób, w jaki wiem, aby zgłosić sprawdzony wyjątek w dynamicznej implementacji interfejsu.
źródło
Thread.stop(Throwable)
bez niebezpieczeństwa, w tym samym wątku i tak możesz cokolwiek rzucić (nie ma testu kompilacji)UnsupportedOperationException
w bieżący wątek od Java 8. Jednak wersja, która rzuca bez argumentów,ThreadDeath
nadal działa.Jednym z nich jest wykorzystanie w
java.util.concurrent.atomic
klasach:źródło
Dla wydajnego kopiowania pamięci (szybsze kopiowanie niż System.arraycopy () przynajmniej dla krótkich bloków); używane przez kodeki Java LZF i Snappy . Używają „getLong” i „putLong”, które są szybsze niż robienie kopii bajt po bajcie; szczególnie wydajny podczas kopiowania takich bloków jak 16/32/64 bajty.
źródło
getLong/putLong
(i trzeba też obliczyć adres)getLong
/putLong
: najlepiej wolałbym ze względuSystem.arraycopy()
na prostotę i wszystko; ale rzeczywiste testy wykazały inaczej w przypadku testowanych przeze mnie przypadków.Niedawno pracowałem nad ponownym wdrożeniem JVM i odkryłem, że pod względem liczby implementacji zaskakuje liczba klas
Unsafe
. Klasa jest głównie zaprojektowana dla implementatorów bibliotek Java i zawiera funkcje, które są zasadniczo niebezpieczne, ale niezbędne do budowania szybkich operacji podstawowych. Na przykład istnieją metody pobierania i zapisywania surowych przesunięć pola, korzystania z synchronizacji na poziomie sprzętowym, przydzielania i zwalniania pamięci itp. Nie jest przeznaczony do użytku przez zwykłych programistów Java; jest nieudokumentowane, specyficzne dla implementacji i z natury niebezpieczne (stąd nazwa!). Co więcej, myślę, żeSecurityManager
uniemożliwi to dostęp do niego w prawie wszystkich przypadkach.Krótko mówiąc, istnieje głównie po to, aby umożliwić implementatorom bibliotek dostęp do komputera bazowego bez konieczności deklarowania każdej metody w niektórych klasach, takich jak
AtomicInteger
natywna. Nie powinieneś używać go ani martwić się o to w rutynowym programowaniu w Javie, ponieważ chodzi o to, aby reszta bibliotek była wystarczająco szybka, abyś nie potrzebował takiego dostępu.źródło
Unsafe.class.getDeclaredField("theUnsafe")
z,.setAccessible(true)
a potem.get(null)
też ją otrzymamUżyj go, aby uzyskać dostęp i wydajnie alokować duże ilości pamięci, na przykład we własnym silniku wokseli! (tj. gra w stylu Minecraft).
Z mojego doświadczenia wynika, że JVM często nie jest w stanie wyeliminować sprawdzania granic w miejscu, którego naprawdę potrzebujesz. Na przykład, jeśli iterujesz po dużej tablicy, ale rzeczywisty dostęp do pamięci jest schowany pod nie-wirtualnym * wywołaniem metody w pętli, JVM może nadal sprawdzać granice przy każdym dostępie do tablicy, a nie tylko raz przed pętla. Tak więc, w celu potencjalnie dużego wzrostu wydajności, możesz wyeliminować sprawdzanie granic JVM w pętli za pomocą metody, która wykorzystuje sun.misc.Unsafe do bezpośredniego dostępu do pamięci, upewniając się, że wykonujesz sprawdzanie granic we właściwych miejscach. (Ty jesteś gonna granice sprawdzić na jakimś poziomie, prawda?)
* przez nie-wirtualny, mam na myśli, że JVM nie powinien dynamicznie rozwiązywać jakiejkolwiek konkretnej metody, ponieważ poprawnie zagwarantowałeś, że klasa / metoda / instancja są kombinacją statycznego / końcowego / what-have-you.
W przypadku mojego domowego silnika wokselowego spowodowało to dramatyczny wzrost wydajności podczas generowania porcji i serializacji (w miejscach, w których odczytywałem / zapisywałem jednocześnie całą tablicę). Wyniki mogą się różnić, ale jeśli Twoim problemem jest brak eliminacji granic, to to rozwiąże.
Występują z tym potencjalnie poważne problemy: w szczególności, gdy zapewnisz dostęp do pamięci bez sprawdzania granic klientom interfejsu, prawdopodobnie wykorzystają to. (Nie zapominaj, że hakerzy mogą być również klientami twojego interfejsu ... szczególnie w przypadku silnika wokseli napisanego w Javie.) Dlatego powinieneś albo zaprojektować swój interfejs, aby nie można było nadużywać dostępu do pamięci, lub należy być bardzo ostrożnym, aby sprawdzić poprawność danych użytkowików zanim można kiedykolwiek, kiedykolwiek mieszają się ze swoim niebezpiecznym interfejsu. Biorąc pod uwagę katastrofalne rzeczy, które haker może zrobić z niekontrolowanym dostępem do pamięci, prawdopodobnie najlepiej jest zastosować oba podejścia.
źródło
Kolekcje zwałowe mogą być przydatne do alokacji ogromnej ilości pamięci i zwolnienia jej natychmiast po użyciu bez zakłóceń GC. Napisałem bibliotekę do pracy z tablicami / listami off-heap opartymi na
sun.misc.Unsafe
.źródło
Wdrożyliśmy ogromne kolekcje, takie jak Arrays, HashMaps, TreeMaps, używając Unsafe.
Aby uniknąć / zminimalizować fragmentację, zaimplementowaliśmy alokator pamięci, używając koncepcji dlmalloc w stosunku do niebezpiecznych.
Pomogło nam to uzyskać wydajność w zakresie współbieżności.
źródło
Unsafe.park()
orazUnsafe.unpark()
do budowy niestandardowych struktur kontroli współbieżności i mechanizmów planowania współpracy.źródło
java.util.concurrent.locks.LockSupport
Nie korzystałem z niej osobiście, ale przypuszczam, że jeśli masz zmienną, która tylko czasami jest odczytywana przez więcej niż jeden wątek (więc tak naprawdę nie chcesz, aby była niestabilna), możesz użyć tego
putObjectVolatile
, pisząc ją w głównym wątku ireadObjectVolatile
podczas wykonywania rzadkich odczytów z innych wątków.źródło
Potrzebujesz go, jeśli chcesz zastąpić funkcjonalność zapewnianą przez jedną z klas, która go obecnie używa.
Może to być niestandardowa / szybsza / bardziej kompaktowa serializacja / deserializacja, szybszy / większy bufor / wersja ByteBuffer o zmiennym rozmiarze lub dodanie zmiennej atomowej, np. Takiej, która nie jest obecnie obsługiwana.
Kiedyś korzystałem z tego do wszystkich.
źródło
Jednym z przykładów jego zastosowania jest metoda losowa, która wywołuje niebezpieczne, aby zmienić ziarno .
Ta strona ma również pewne zastosowania .
źródło
Obiekt wydaje się być dostępny do pracy na niższym poziomie niż zwykle pozwala na to kod Java. Jeśli kodujesz aplikację wysokiego poziomu, JVM wyodrębnia obsługę pamięci i inne operacje z dala od poziomu kodu, aby ułatwić programowanie. Korzystając z niebezpiecznej biblioteki, skutecznie wykonujesz operacje niskiego poziomu, które zwykle byłyby dla Ciebie wykonane.
Jak stwierdził woliveirajr, „random ()” używa Unsafe do inicjowania, podobnie jak wiele innych operacji będzie używać funkcji replaceateMemory () zawartej w Unsafe.
Jako programista prawdopodobnie mógłbyś uniknąć tej biblioteki, ale ścisła kontrola nad elementami niskiego poziomu przydaje się (dlatego wciąż istnieje kod asemblera i (w mniejszym stopniu) kod C przemieszczający się w głównych produktach)
źródło