前几天,我遇到了sun.misc.Unsafe软件包,并对它可以做什么感到惊讶。
当然,该类是未记录的,但是我想知道是否有充分的理由使用它。在需要使用它的地方会出现什么情况?在现实世界中如何使用它?
此外,如果您这样做需要它,这是否表示您的设计可能存在问题?
为什么Java甚至包括此类?
前几天,我遇到了sun.misc.Unsafe软件包,并对它可以做什么感到惊讶。
当然,该类是未记录的,但是我想知道是否有充分的理由使用它。在需要使用它的地方会出现什么情况?在现实世界中如何使用它?
此外,如果您这样做需要它,这是否表示您的设计可能存在问题?
为什么Java甚至包括此类?
Answers:
例子
VM“化身”。即无锁哈希表中使用的CAS(比较和交换),例如:sun.misc.Unsafe.compareAndSwapInt,它可以对包含针对CAS的特殊指令的本机代码进行真正的JNI调用
在此处阅读有关CAS的更多信息http://en.wikipedia.org/wiki/Compare-and-swap
主机VM的sun.misc.Unsafe功能可用于分配未初始化的对象,然后将构造函数调用解释为任何其他方法调用。
可以从本机地址跟踪数据。可以使用java.lang.Unsafe类检索对象的内存地址,并通过不安全的get / put方法直接在其字段上进行操作!
JVM的编译时间优化。使用“魔术”的高性能VM,需要低级操作。例如:http : //en.wikipedia.org/wiki/Jikes_RVM
分配内存sun.misc.Unsafe.allocateMemory,例如:-调用ByteBuffer.allocateDirect时,DirectByteBuffer构造函数在内部对其进行调用
跟踪调用堆栈并重播由sun.misc实例化的值。不安全,对检测很有用
sun.misc.Unsafe.arrayBaseOffset和arrayIndexScale可用于开发arraylet,该技术可将大型数组有效地分解为较小的对象,从而限制了对大型对象进行扫描,更新或移动操作的实时成本
http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java
有关参考的更多信息-http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html
通过在某些代码搜索引擎中运行搜索,我得到以下示例:
用于访问{@link Unsafe}对象的简单类。{@link Unsafe} *是必需的,以允许对阵列进行有效的CAS操作。请注意,{@ link java.util.concurrent.atomic}中的版本,例如{@link java.util.concurrent.atomic.AtomicLongArray},需要额外的内存排序保证,这些算法通常不需要这些保证,而且价格昂贵在大多数处理器上。
/ **静态字段的基于sun.misc.Unsafe的FieldAccessors的基类。从反射代码的角度来看,观察到只有九种类型的字段:八种基本类型和对象。使用Unsafe类代替生成的字节码可以节省动态生成的FieldAccessor的内存和加载时间。* /
/ *通过导线发送的FinalFields ..如何在接收端解组和重新创建对象?我们不想调用构造函数,因为它将为最终字段建立值。我们必须完全像发送方一样重新创建最终字段。sun.misc.Unsafe为我们做到了。* /
还有很多其他示例,只需点击上面的链接即可...
有趣的是,我什至从未听说过这堂课(这确实是一件好事)。
让人想起的一件事是使用Unsafe#setMemory将包含敏感信息(密码,键等)的缓冲区归零。您甚至可以对“不可变”对象的字段执行此操作(然后再次,我认为普通的老式反射也可以在此处完成此操作)。我不是安全专家,所以请带一点盐。
I'd never even heard of this class
...我已经告诉过你很多次了!叹息 + :(
park()
文档:“阻塞当前线程,在发生平衡解除停车,或者已经发生平衡解除停车或线程中断时返回,或者,如果不是绝对的并且时间不为零,则返回给定的时间(以毫微秒为单位)过去了,或者如果是绝对的话,则是自Epoch过去以来的给定截止时间(以毫秒为单位),或者是虚假的(即,没有“理由”返回)。几乎与“程序退出时释放内存,或以随机间隔,以先到者为准”释放内存一样好。
基于对Java 1.6.12库的简要分析(使用eclipse进行引用跟踪),似乎Eclipse的每个有用功能 Unsafe
都以有用的方式公开。
CAS操作通过Atomic *类公开。内存操作功能通过DirectByteBuffer公开。Sync指令(park,unpark)通过AbstractQueuedSynchronizer公开,而LockQueue实现则使用它。
LockSupport
不是通过AQS 进行公开(后者比停放/
Unsafe.throwException-允许抛出已检查的异常而无需声明它们。
在处理反射或AOP的某些情况下,这很有用。
假设您为用户定义的接口构建通用代理。用户可以通过在接口中声明异常来指定在特殊情况下实现会抛出哪个异常。这是我知道的唯一方法,可以在接口的动态实现中引发受检查的异常。
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();
}
}
Thread.stop(Throwable)
不需要不安全的操作,在同一线程中可以进行任何操作(没有编译检查)
UnsupportedOperationException
Java 8开始,该方法已被拔出并在当前线程中抛出。但是,抛出该参数的无参数版本ThreadDeath
仍然有效。
有效的内存复制(对于短块而言,比System.arraycopy()的复制速度更快);由Java LZF和Snappy编解码器使用。他们使用“ getLong”和“ putLong”,这比逐字节进行复制要快。复制16/32/64字节块之类的文件时特别有效。
getLong/putLong
(你要计算的地址,以及)
getLong
/ 时,putLong
我会在x86-64上始终看到更好的性能:理想情况下,我会更倾向于System.arraycopy()
简单和全部;但是对于我测试过的情况,实际测试却显示出其他含义。
我最近正在重新实现JVM,发现在方面实现了数量惊人的类Unsafe
。该类主要是为Java库实现者设计的,并且包含本质上不安全的功能,但对于构建快速基元而言是必需的。例如,存在一些获取和写入原始字段偏移量,使用硬件级同步,分配和释放内存等的方法。普通Java程序员不打算使用它。它是无证的,特定于实现的,并且本质上是不安全的(因此得名!)。而且,我认为SecurityManager
在几乎所有情况下,都将禁止使用它。
简而言之,主要是允许库实现者访问底层计算机,而不必在某些类(例如AtomicInteger
native)中声明每个方法。您不需要在常规的Java编程中使用或担心它,因为重点是使其余库足够快,以至于您不需要这种访问。
Unsafe.class.getDeclaredField("theUnsafe")
以.setAccessible(true)
然后.get(null)
将得到它太
使用它可以有效地访问和分配大量内存,例如在您自己的体素引擎中!(即Minecraft风格的游戏。)
以我的经验,JVM经常无法消除在真正需要它的地方进行边界检查。例如,如果您要遍历一个大数组,但是实际的内存访问位于循环中的非virtual *方法调用之下,则JVM可能仍会对每个数组访问执行边界检查,而不是仅在执行一次之前循环。因此,为了获得可能的较大性能,您可以通过使用sun.misc.Unsafe直接访问内存的方法来消除循环内的JVM边界检查,从而确保在正确的位置进行任何边界检查。(你是要去边界检查在一定程度上,对吧?)
*通过非虚拟方式,我的意思是JVM不必动态地解析您的特定方法,因为您已经正确地保证了类/方法/实例是静态/最终/您所需要的某种组合。
对于我自己开发的体素引擎,这在块生成和序列化(现在是我一次读取/写入整个阵列的位置)期间带来了显着的性能提升。结果可能会有所不同,但是如果您遇到的问题是缺少消除边界的方法,则可以解决此问题。
这有一些潜在的主要问题:特别是,当您提供无限制地检查接口客户端的访问内存的功能时,他们可能会滥用它。(别忘了黑客也可以成为您接口的客户端……尤其是在用Java编写的体素引擎的情况下。)因此,您应该以不滥用内存访问的方式设计接口,或者你应该非常小心,以验证用户的数据,才可以永远,永远与你的危险接口打成一片。考虑到黑客可以对未经检查的内存访问进行灾难性的处理,最好同时采用两种方法。
我自己还没有使用过它,但是我想如果您有一个仅偶尔被一个以上线程读取的变量(因此,您真的不想使其变得易变),则可以putObjectVolatile
在主线程中使用它时使用readObjectVolatile
从其他线程进行稀有读取时。
其用法的一个示例是random方法,该方法调用不安全的方法来更改seed。