Sun.misc.Usafefe neden var ve gerçek dünyada nasıl kullanılabilir? [kapalı]


267

Geçen gün sun.misc.Unsafe paketine rastladım ve neler yapabileceğine hayran kaldım.

Tabii ki, sınıf belgelenmemiş, ama onu kullanmak için iyi bir neden olup olmadığını merak ediyordum. Kullanmanız gereken yerlerde hangi senaryolar ortaya çıkabilir? Gerçek dünya senaryosunda nasıl kullanılabilir?

Eğer Dahası, bunu ihtiyacını o şey muhtemelen tasarımı ile yanlış göstermez o yapar?

Java neden bu sınıfı içeriyor?


7
JDK geliştiricileri şu anda Java 9'da herkese açık bir API'ya dönüşüm için bu API'yı inceliyorlar . Kullanıyorsanız , anketi doldurmak 5 dakika sürmeye değer: surveymonkey.com/s/sun-misc-Unsafe .
Andy Lynch

2
Bu yazı meta üzerinde tartışılıyor: meta.stackoverflow.com/questions/299139/…
Jon Clements

Yanıtlar:


159

örnekler

  1. VM "intrinsifikasyon." Kilitsiz Karma Tablolarda kullanılan CAS (Karşılaştırma ve Değiştirme) Örn: sun.misc.Unsafe.compareAndSwapInt, CAS için özel talimatlar içeren yerel koda gerçek JNI çağrıları yapabilir

    CAS hakkında daha fazla bilgiyi http://en.wikipedia.org/wiki/Compare-and-swap

  2. Ana makine VM'sinin sun.misc.Unsafe işlevselliği, başlatılmamış nesneleri ayırmak ve daha sonra yapıcı çağrısını başka bir yöntem çağrısı olarak yorumlamak için kullanılabilir.

  3. Veriler yerel adresten takip edilebilir. Java.lang.Unsafe sınıfını kullanarak bir nesnenin bellek adresini almak ve güvenli olmayan alma / koyma yöntemleri ile kendi alanlarında doğrudan çalışmak mümkündür!

  4. JVM için zaman optimizasyonlarını derleyin. Düşük seviye operasyonlar gerektiren "sihirli" kullanan yüksek performanslı VM. ör .: http://en.wikipedia.org/wiki/Jikes_RVM

  5. Bellek ayırma, sun.misc.Unsafe.allocateMemory örn .: - ByteBuffer.allocateDirect çağrıldığında DirectByteBuffer yapıcısı dahili olarak çağırır

  6. Çağrı yığınının izlenmesi ve sun.misc tarafından başlatılan değerlerle tekrar çalma Güvenli olmayan, enstrümantasyon için yararlı

  7. sun.misc.Unsafe.arrayBaseOffset ve arrayIndexScale, büyük dizilerin gerçek zamanlı tarama, güncelleme veya taşıma maliyetlerini sınırlamak için büyük dizileri daha küçük nesnelere verimli bir şekilde bölmek için kullanılan bir teknik olan diziler geliştirmek için kullanılabilir

  8. http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java

buradaki referanslar hakkında daha fazla bilgi - http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html


1
Güvenli olmayan bir alanın adresini alırsanız, GC tarafından her zaman değiştirilebilir, bu nedenle bu işlem oldukça işe yaramaz mı?
pdeva

tahsis ettiklerinizin adresini alın
zudokod

Tam olarak tek ne demek istiyorsunuz ben tahsis etmişlerdir. Bu, 'yeni' operatör kullanılarak nesnelerin yaratıldığı yerlerde kullanılıyor, dolayısıyla sorum.
pdeva

1
unsafe.allocateMemory ve değeri koymak
zudokod

1
Nokta 2 ile ilgili olarak, kurucuyu başka bir yöntem çağrısı olarak nasıl çağırabileceğinizi bilmek isterim? Çünkü bayt kodlar olmadan bunu yapmanın bir yolunu bulamadım.
Miguel Gamboa

31

Sadece bazı kod arama motorunda bir arama yaparak aşağıdaki örnekleri alırım:

{@Link Güvensiz} nesnesine erişmek için basit sınıf. Dizilerde verimli CAS işlemlerine izin vermek için {@link Güvensiz} * gereklidir. {@Link java.util.concurrent.atomic} içindeki {@link java.util.concurrent.atomic.AtomicLongArray} gibi sürümlerin, bu algoritmalarda genellikle gerekli olmayan ve ayrıca pahalı olan ek bellek siparişi garantileri gerektirdiğini unutmayın. çoğu işlemcide.

/ ** sun.misc.Usafe tabanlı FieldAccessors için statik sınıflar için temel sınıf. Gözlem, yansıma kodu açısından sadece dokuz tür alanın olmasıdır: sekiz ilkel tür ve Nesne. Oluşturulan bayt kodları yerine Sınıf Güvenli Olmayan'ı kullanmak, dinamik olarak oluşturulan FieldAccessors için bellek ve yükleme süresi kazandırır. * /

  • SpikeSource

/ * Tel üzerinden gönderilen FinalField'lar .. alıcı taraftaki nesnenin nasıl kaldırılacağı ve yeniden oluşturulacağı? Nihai alanlar için değerler oluşturacağından kurucuyu çağırmak istemiyoruz. Nihai alanı tam olarak gönderen tarafında olduğu gibi yeniden yaratmalıyız. Sun.misc.Unsafe bunu bizim için yapıyor. * /

Diğer birçok örnek var, yukarıdaki bağlantıyı takip edin ...


25

İlginçtir, bu sınıfı hiç duymamıştım (muhtemelen iyi bir şeydir).

Akla gelen bir şey , bir noktada hassas bilgiler içeren (parolalar, anahtarlar, ...) arabellekleri sıfırlamak için Güvenli Olmayan # setMemory kullanmaktır . Hatta bunu "değişmez" nesnelerin alanlarına bile yapabilirsiniz (o zaman tekrar sade eski yansıma burada da hile yapabilir). Ben bir güvenlik uzmanı değilim, bu yüzden bunu bir tuz tanesi ile alın.


4
I'd never even heard of this class... size birçok kez bahsetmiştim! iç çekiş + :(
Tim Bender

7
Herhangi bir noktaya değinmeyecekti, çünkü Java bir kopyalayıcı nesil çöp toplayıcı kullanıyor ve hassas bilgileriniz muhtemelen üzerine yazılmayı bekleyen 'boş' bellekte başka bir yere yerleştirilecek.
Daniel Cassidy

39
Bunu da hiç duymadım, ama park()belgelerini çok seviyorum : "Mevcut iş parçacığını engelle, bir dengeleyici park kaldırıldığında veya bir dengeleyici park kaldırılmışsa veya iş parçacığı kesilmişse veya mutlak ve zaman sıfır değilse, belirli bir zaman nanosaniye geçti, ya da mutlaksa , Epoch geçtiği ya da sahte (yani, 'sebepsiz' için geri dönüyor) olduğu için verilen son tarih milisaniye cinsinden ". Neredeyse "program çıktığında veya rastgele aralıklarla hangisi önce gelirse bellek boşaltılır" kadar iyidir.
aroth

1
@Daniel, ilginç, bunu düşünmemiştim. Şimdi neden bir güvenlik uzmanı olmadığımı görebilirsiniz. :)
Mike Daniels

22

Referans takibi için tutulma kullanan Java 1.6.12 kitaplığının çok kısa bir analizine dayanarak, her yararlı işlevselliği Unsafeyararlı şekillerde ortaya çıkıyor gibi görünüyor .

CAS işlemleri Atom * sınıfları aracılığıyla ortaya çıkar. Bellek işleme işlevleri DirectByteBuffer Sync yönergeleri (park, park yeri kaldırma) aracılığıyla açıklanır ve buna karşılık Lock uygulamaları tarafından kullanılan AbstractQueuedSynchronizer aracılığıyla maruz bırakılır.


AtomicXXXUpdaters çok yavaş ve gerçekten ihtiyacınız olduğunda: CAS - onları gerçekten kullanamazsınız. Eğer metali yapacaksanız, soyutlama seviyelerini ve çok sayıda kontrolü kullanmayacaksınız. CAS başarısız olursa bir döngü esp. donanım dalı (yüksek çekişme nedeniyle) yanlış tahmin etmeye karar verdiğinde, ancak daha az karşılaştırma / dal olması acı veriyor. Park / Unpark, AQS'den LockSupportdeğil (bu ikincisi park / unpark'tan daha fazla kilit içeriğidir)
bestsss

21

Unsafe.throwException - işaretli istisnayı bildirmeden atmayı sağlar.

Bu, yansıma veya AOP ile uğraştığınız bazı durumlarda yararlıdır.

Kullanıcı tanımlı bir Arayüz için genel bir proxy oluşturduğunuzu varsayın. Ve kullanıcı, sadece istisnaları arayüzde bildirerek özel bir durumda uygulama tarafından hangi istisnanın atılacağını belirleyebilir. O zaman, Arayüzün Dinamik Uygulamasında kontrol edilen bir istisnayı yükseltmenin tek yolu budur.

import org.junit.Test;
/** need to allow forbidden references! */ import sun.misc.Unsafe;

/**
 * Demonstrate how to throw an undeclared checked exception.
 * This is a hack, because it uses the forbidden Class {@link sun.misc.Unsafe}.
 */
public class ExceptionTest {

    /**
     * A checked exception.
     */
    public static class MyException extends Exception {
        private static final long serialVersionUID = 5960664994726581924L;
    }

    /**
     * Throw the Exception.
     */
    @SuppressWarnings("restriction")
    public static void throwUndeclared() {
        getUnsafe().throwException(new MyException());
    }

    /**
     * Return an instance of {@link sun.misc.Unsafe}.
     * @return THE instance
     */
    @SuppressWarnings("restriction")
    private static Unsafe getUnsafe() {
        try {

            Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe");
            singleoneInstanceField.setAccessible(true);
            return (Unsafe) singleoneInstanceField.get(null);

        } catch (IllegalArgumentException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (SecurityException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (NoSuchFieldException e) {
            throw createExceptionForObtainingUnsafe(e);
        } catch (IllegalAccessException e) {
            throw createExceptionForObtainingUnsafe(e);
        }
    }

    private static RuntimeException createExceptionForObtainingUnsafe(final Throwable cause) {
        return new RuntimeException("error while obtaining sun.misc.Unsafe", cause);
    }


    /**
     * scenario: test that an CheckedException {@link MyException} can be thrown
     * from an method that not declare it.
     */
    @Test(expected = MyException.class)
    public void testUnsingUnsaveToThrowCheckedException() {
        throwUndeclared();
    }
}

14
Aynı Thread.stop(Throwable)güvensiz gerek yok yapabilirsiniz, aynı iş parçacığında yine de bir şey atabilirsiniz (derleme kontrolü yoktur)
bestsss

Bunu sadece bayt kodu ile yapabilirsiniz (Veya sizin için bunu yapmak için Lomboc kullanın)
Antimon

1
@bestsss Bu yöntem saptandı ve UnsupportedOperationExceptionJava 8'den itibaren geçerli iş parçacığına atar . Ancak, atanan bağımsız değişken sürümü ThreadDeathhala çalışır.
gparyani

@damryfbfnetsi, oldukça uzun bir süredir temel jdk tartışmalarını takip etmedim ve java 8'e geçmeyi planlamıyorum. Yine de, doğrulayıcı gerçekte olup olmadığını kontrol etmedikçe, bu oldukça şaşırtıcı bir fikirdir. yöntemi, tek kullanımlık malzemeleri beyan eder ... ancak atılan istisna hakkındaki meta veriler atılmak serbest olduğundan geriye doğru uyumsuz olabilir.
bestsss

10

Sınıf Güvensiz

Düşük seviyeli, güvensiz işlemleri gerçekleştirmek için bir yöntem koleksiyonu. Sınıf ve tüm yöntemler herkese açık olsa da, bu sınıfın kullanımı sınırlıdır, çünkü yalnızca güvenilir kod örnekleri alabilir.

Bunun bir kullanımı java.util.concurrent.atomicsınıflardadır:


6

Verimli bellek kopyası için (en azından kısa bloklar için System.arraycopy () 'den daha hızlı kopyalama); Java LZF ve Snappy codec bileşenleri tarafından kullanılır . Byte byte byte kopya yapmaktan daha hızlı olan 'getLong' ve 'putLong' kullanıyorlar; özellikle 16/32/64 bayt blokları kopyalarken etkilidir.


1
Doh, arraycopy, x86-64 üzerinde daha iyi olan SSE döngülerini kullanır getLong/putLong(ve adresi de hesaplamanız gerekir)
bestsss

Bunu gerçekten ölçtünüz mü? Daha kısa bloklar için getLong/ kombinasyonunu kullanırken x86-64 üzerinde sürekli olarak daha iyi performans görüyorum putLong: ideal System.arraycopy()olarak basitlik ve her şeyi tercih ederim ; ancak gerçek testler test ettiğim durumlar için aksini gösterdi.
StaxMan

evet güvensiz kullanarak deflate impl hiçbir anlamlı performans olamazdı. Büyük dizilerde birkaç baytlık uzun kopyalar için get / putLong derleyici uzunlukları kontrol etmek zorunda kaldığında gerçekten işe yarayabilir. Bazı impl. System.arrayCopy'den bellek çiti ekleyin (ancak devre dışı bırakılabilir / etkinleştirilebilir), böylece gerçek suçlu olabilir.
bestsss

Tamam. Daha yeni JDK'ların bunu değiştirmesi mümkündür; Aslında daha hızlı bir işlem gözlemlediğimde (JDK 1.6 ile) ben de şaşırdım. Ya da belki de kullanımdaki belirli bir farkı unutuyorum. Bunlar, iş yaparken bile zor (ve muhtemelen kararsız) optimizasyonlardır ve etkileri ölçmek önemlidir.
StaxMan

5

Son zamanlarda JVM'yi yeniden uygulamak için çalışıyordum ve açısından şaşırtıcı sayıda sınıfın uygulandığını gördüm Unsafe. Sınıf çoğunlukla Java kitaplığı uygulayıcıları için tasarlanmıştır ve temelde güvenli olmayan ancak hızlı ilkel öğeler oluşturmak için gerekli olan özellikler içerir. Örneğin, ham alan ofsetleri alma ve yazma, donanım düzeyinde senkronizasyon kullanma, bellek ayırma ve boşaltma vb. Yöntemleri vardır. Normal Java programcıları tarafından kullanılması amaçlanmamıştır; belgesiz, uygulamaya özgü ve doğal olarak güvensizdir (dolayısıyla adı!). Dahası, bence SecurityManagerneredeyse her durumda ona erişimi yasaklıyor.

Kısacası, kütüphane uygulayıcılarının AtomicIntegeryerel gibi belirli sınıflardaki her yöntemi bildirmek zorunda kalmadan temeldeki makineye erişmesine izin vermek vardır . Bütün mesele kütüphanelerin geri kalanını bu tür bir erişime ihtiyacınız olmayacak kadar hızlı hale getirmek olduğundan, rutin Java programlamasında kullanmanız veya endişelenmeniz gerekmez.


aslında, SecurityManager yalnızca yansıma devre dışı bırakıldığında erişimi engellemez
amara

@ sparkleshy- Bu konuyu biraz açıklayabilir misiniz?
templatetypedef

getUnsafe gelen bir örneğini almak oldukça sıkı gereksinimleri, var ise Unsafe.class.getDeclaredField("theUnsafe")ile .setAccessible(true)daha sonra ve .get(null)de onu alacak
amara

@ sparkleshy- Çalıştığına şaşırdım - güvenlik yöneticisi bunu işaretliyor olmalı.
templatetypedef

5

Kendi voksel motorunuzda olduğu gibi yüksek miktarda belleğe verimli bir şekilde erişmek ve bunları ayırmak için kullanın! (yani Minecraft tarzı oyun.)

Deneyimlerime göre, JVM, gerçekten ihtiyacınız olan yerde sınır kontrolünü ortadan kaldıramaz. Örneğin, büyük bir dizi üzerinden yineleme yapıyorsanız, ancak gerçek bellek erişimi döngüdeki sanal olmayan bir * yöntem çağrısının altına sıkışmışsa, JVM yine de bir kez değil, her dizi erişimiyle bir sınır denetimi gerçekleştirebilir döngü. Böylece, potansiyel olarak büyük performans kazanımları için, döngü içinde JVM sınırlarını kontrol etmeyi sun.misc.Icfefe kullanan bir yöntemle doğrudan belleğe erişerek, kendinizi doğru yerlerde kontrol ederek emin olun. (Sen şunlardır olacak sınırları doğru, bazı düzeyde kontrol?)
* Sanal olmayan bir ifadeyle, JVM'nin özel yönteminiz ne olursa olsun dinamik olarak çözmesi gerekmediği anlamına gelir, çünkü sınıf / yöntem / örneğin statik / final / neyin var olduğunu doğru bir şekilde garanti ettiğinden emin olabilirsiniz.

Evde yetiştirilen voksel motorum için bu, yığın oluşturma ve serileştirme sırasında (tüm diziye aynı anda okuduğum / yazdığım yerlerin) dramatik bir performans kazancı sağladı. Sonuçlar değişebilir, ancak sınırların ortadan kaldırılması sorununuz varsa, bu sorunu çözecektir.

Bununla ilgili bazı önemli sorunlar vardır: özellikle, arabiriminizin istemcilerine sınır denetimi yapmadan belleğe erişebilmenizi sağladığınızda, muhtemelen kötüye kullanırlar. (Bilgisayar korsanlarının arayüzünüzün istemcileri olabileceğini de unutmayın ... özellikle Java ile yazılmış bir voksel motoru durumunda.) Bu nedenle, arayüzünüzü bellek erişiminin kötüye kullanılamayacağı şekilde tasarlamalısınız veya Her zamankinden çok, önce kullanıcı verileri doğrulamak için son derece dikkatli olmalıdır şimdiye sizin tehlikeli arayüzü ile karışmak. Bir bilgisayar korsanının denetlenmeyen bellek erişimiyle yapabileceği yıkıcı şeyler göz önüne alındığında, muhtemelen her iki yaklaşımı da kullanmak en iyisidir.


4

Yığın dışı koleksiyonlar, GC müdahalesi olmadan kullanımdan hemen sonra çok miktarda bellek tahsis etmek ve yeniden yerleştirmek için yararlı olabilir. Yığın dışı diziler / listeler ile çalışmak için bir kütüphane yazdım sun.misc.Unsafe.


4

Güvenli Olmayan'ı kullanarak Diziler, HashMaps, TreeMaps gibi büyük koleksiyonlar uyguladık.
Parçalanmayı önlemek / en aza indirmek için, dlmalloc kavramlarını güvensiz olarak kullanarak bellek ayırıcıyı uyguladık .
Bu, eşzamanlılıktaki performansı kazanmamıza yardımcı oldu.


3

Unsafe.park()ve Unsafe.unpark()özel eşzamanlılık kontrol yapılarının ve kooperatif zamanlama mekanizmalarının inşası için.


24
herkese açık olarak kullanılabilirjava.util.concurrent.locks.LockSupport
bestsss

1

Kendim kullanmadım, ancak sadece zaman zaman birden fazla iş parçacığı tarafından okunan bir değişkeniniz varsa (bu yüzden gerçekten uçucu hale getirmek istemezsiniz) putObjectVolatile, ana iş parçacığında yazarken kullanabilirsiniz ve readObjectVolatilediğer konulardan nadir okumalar yaparken.


1
ancak aşağıdaki konudaki tartışmaya göre, kaplamasız uçucu neredeyse uçucu olmayanlar kadar hızlıdır stackoverflow.com/questions/5573782/…
pdeva

uçucu semantikleri düz yazma ve uçucu okumalarla değiştiremezsiniz ... bu bir afet için bir reçetedir, çünkü bir ayarda çalışabilir, diğerinde çalışamaz. Eğer okuyucu üzerinde size yazı parçacığı üzerinde AtomicReference.lazySet kullanabilir ve alabilirsiniz tek yazar parçacığı () ile uçucu anlambilgisi isteyen varsa (bunu görmek yazıyı konuyla ilgili bir tartışma için). Uçucu okumalar nispeten ucuzdur, ancak ücretsiz değildir, buraya bakın .
Nitsan Wakart

“... putObjectVolatile'ı yazarken kullanabilirsiniz ...” Düz yazılar önermiyordum.
Matt Crinklaw-Vogt

1

Şu anda kullanan sınıflardan biri tarafından sağlanan işlevselliği değiştirmeniz gerekiyorsa, buna ihtiyacınız vardır.

Bu özel / daha hızlı / daha kompakt serileştirme / serileştirme, ByteBuffer'ın daha hızlı / daha büyük bir tamponu / yeniden boyutlandırılabilir versiyonu veya bir atom değişkeni eklenebilir, örneğin şu anda desteklenmeyen bir değişken.

Bir süredir tüm bunlar için kullandım.



0

Nesne, genellikle Java kodunun izin verdiğinden daha düşük bir seviyede çalışmaya hazır görünmektedir. Yüksek düzeyde bir uygulama kodluyorsanız, JVM bellek işlemeyi ve diğer işlemleri kod seviyesinden soyutlar, böylece programlanması daha kolaydır. Güvenli Olmayan kütüphaneyi kullanarak, genellikle sizin için yapılacak olan düşük düzeyli işlemleri etkili bir şekilde tamamlamış olursunuz.

Woliveirajr'ın belirttiği gibi "random ()", diğer birçok işlemin Güvenli olmama dahilindeki SepateMemory () işlevini kullanacağı gibi tohumlamak için Güvenli Değil kullanır.

Bir programcı olarak muhtemelen bu kütüphaneye hiç ihtiyaç duymadan kurtulabilirsiniz, ancak düşük seviyeli elemanlar üzerinde sıkı bir kontrole sahip olmak kullanışlı hale gelir (bu yüzden hala Meclis ve (daha az ölçüde) büyük ürünlerde dolaşan C kodu vardır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.