sun.misc.Unsafe为什么存在,在现实世界中如何使用?

前几天我偶然发现了一个不安全的包装,它的功能让我感到惊讶。

当然,这个类是没有记录的,但是我想知道是否有一个很好的理由使用它。可能会出现哪些需要使用它的场景?如何在现实场景中使用它?

此外,如果你需要它,这不是表明你的设计可能有问题吗?

为什么Java包含这个类?

60926 次浏览

我自己没有使用过它,但我认为如果你有一个变量,只是偶尔被多个线程读取(所以你真的不想让它volatile),你可以在主线程中写入它时使用putObjectVolatile,在从其他线程中进行罕见的读取时使用readObjectVolatile

不安全的

用于执行低级、不安全操作的方法的集合。虽然类和所有方法都是公共的,但该类的使用是有限的,因为只有可信代码才能获得它的实例。

它的一个用法是在java.util.concurrent.atomic类中:

有趣的是,我甚至从未听说过这个类(这可能是一件好事,真的)。

首先想到的一件事是使用不安全# setMemory来在某一时刻将包含敏感信息(密码,密钥,…)的缓冲区归零。您甚至可以对“不可变”对象的字段执行此操作(然后,我再次假设普通的反射也可以在这里执行此操作)。但我不是安全专家,所以对此持保留态度。

Unsafe.park()Unsafe.unpark()用于构造自定义并发控制结构和协作调度机制。

通过在一些代码搜索引擎中运行搜索,我得到了以下示例:

获取访问{@link Unsafe}对象的简单类。{@link安全} 为了允许对数组进行有效的CAS操作,*是必需的。注意 {@link java.util.concurrent中的版本。原子},例如{@link java.util.concurrent.atomic。AtomicLongArray},需要额外的内存排序 这些保证在这些算法中通常是不需要的

/** sun.misc基类基于静态的不安全的FieldAccessors 字段。据观察,只有9种类型的 从反射代码的角度看字段:八个原语 类型和对象。使用不安全类而不是生成类 字节码节省内存和加载时间 动态生成的FieldAccessors。* / < / p >

  • SpikeSource
< p > / * 通过线路发送的FinalFields ..如何解编和重新创建对象上 接收方?我们不想调用构造函数,因为它将为 最后一个字段。我们必须重新创建与发送端完全相同的最终字段。 太阳,杂项,不安全为我们做了这些。 * / < / p >

还有很多其他的例子,只要按上面的链接…

基于对使用eclipse进行引用跟踪的Java 1.6.12库的简要分析,似乎Unsafe的每个有用功能都以有用的方式公开了。

CAS操作通过Atomic*类公开。 内存操作函数通过DirectByteBuffer公开 同步指令(park,unpark)通过AbstractQueuedSynchronizer公开,而AbstractQueuedSynchronizer又由Lock实现使用

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();
}
}

例子

  1. VM "intrinsification."即在无锁哈希表中使用的CAS(比较和交换) 例如:sun.misc.Unsafe.compareAndSwapInt 它可以对包含CAS

    特殊指令的本机代码进行真正的JNI调用

    阅读更多关于CAS在这里http://en.wikipedia.org/wiki/Compare-and-swap

  2. 主机VM的sun.misc.Unsafe功能可用于分配未初始化的对象,然后将构造函数调用解释为任何其他方法调用。

  3. One可以从本机地址开始跟踪数据。可以检索 对象的内存地址使用java.lang.Unsafe类,并直接通过不安全的get/put方法操作它的字段!李< / p > < / >

  4. JVM的编译时优化。高性能虚拟机使用“魔法”,需要低级操作。eg: http://en.wikipedia.org/wiki/Jikes_RVM

  5. - DirectByteBuffer构造函数内部调用它时,ByteBuffer。调用allocateDirect

  6. 跟踪调用堆栈并使用sun.misc实例化的值重放。不安全,但用于仪器

  7. arraybaseoffset和arrayIndexScale可用于开发arraylet,这是一种有效地将大型数组分解为较小对象的技术,以限制对大型对象进行扫描、更新或移动操作的实时成本

  8. http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java < a href = " http://robaustin.wikidot.com/how-to-write-to-direct-memory-locations-in-java " > < / >

更多关于这里的引用- http://bytescrolls.blogspot.com/2011/04/interesting-uses-of-sunmiscunsafe.html

它的使用的一个例子是随机方法,调用不安全的更改种子.;

这个网站也有它的一些用途

对象的可用性似乎低于Java代码通常允许的级别。如果您正在编写一个高级应用程序,那么JVM将内存处理和其他操作从代码级别抽象出来,因此更容易编程。通过使用不安全库,您可以有效地完成通常由您完成的低级操作。

正如woliveirajr所述,“random()”使用Unsafe来播种,就像许多其他操作将使用Unsafe中包含的allocateMemory()函数一样。

作为程序员,你可能永远不需要这个库,但严格控制底层元素确实很方便(这就是为什么在主要产品中仍然有汇编代码和(在较小程度上)C代码的原因)

我最近在重新实现JVM,发现数量惊人的类是根据Unsafe实现的。这个类主要是为Java库实现者设计的,它包含一些基本不安全的特性,但对于构建快速原语是必需的。例如,有一些方法用于获取和写入原始字段偏移量、使用硬件级同步、分配和释放内存等。它不是供普通Java程序员使用的;它是无文档的、特定于实现的,并且本质上不安全(因此得名!)此外,我认为SecurityManager将不允许在几乎所有情况下访问它。

简而言之,它的存在主要是为了允许标准库实现者访问底层机器,而不必在某些类(如AtomicInteger native)中声明每个方法。在常规Java编程中不需要使用或担心它,因为重点是使其余的库足够快,从而不需要这种访问。

如果您需要替换当前使用它的某个类所提供的功能,则需要使用它。

这可以是自定义/更快/更紧凑的序列化/反序列化,一个更快/更大的缓冲区/可调整大小的ByteBuffer版本,或者添加一个原子变量,例如当前不支持的。

我曾经用它来处理所有这些问题。

为了有效地复制内存(至少对于短块,复制速度比System.arraycopy()快);由Java LZF时髦的编解码器使用。它们使用'getLong'和'putLong',这比逐字节复制要快;在复制16/32/64字节块时尤其有效。

堆外集合对于分配大量内存并在使用后立即释放内存而不受GC干扰可能很有用。我基于sun.misc.Unsafe编写了一个图书馆用于处理堆外数组/列表。

使用它来有效地访问和分配大量的内存,例如在您自己的体素引擎中!(例如《我的世界》风格的游戏。)

根据我的经验,JVM通常无法在您真正需要时消除边界检查。例如,如果在一个大数组上迭代,但实际的内存访问隐藏在循环中的非虚拟*方法调用之下,JVM仍然可能对每个数组访问执行边界检查,而不是在循环之前执行一次。因此,为了获得潜在的性能提升,您可以通过使用sun.misc.Unsafe直接访问内存的方法来消除循环内的JVM边界检查,确保自己在正确的位置执行任何边界检查。(你将在某个级别检查边界,对吗?)
*所谓的非虚,我的意思是JVM不应该动态解析你的特定方法,因为你已经正确地保证了类/方法/实例是静态/final/what-have-you.

对于我自己开发的体素引擎来说,这在块生成和序列化期间(在我同时读取/写入整个数组的低位置)带来了显著的性能提升。结果可能会有所不同,但如果缺乏界限消除是您的问题,那么这将解决它。

这有一些潜在的主要问题:具体来说,当您向接口的客户端提供不进行限制检查的访问内存的能力时,他们可能会滥用它。(别忘了黑客也可以是你界面的客户端……特别是在用Java编写的体素引擎的情况下。)因此,你应该以一种不能滥用内存访问的方式设计你的接口,或者你应该非常小心地在用户数据混入你的危险接口之前验证用户数据。考虑到黑客在未经检查的内存访问中可能造成的灾难性后果,最好是采用这两种方法。

我们使用Unsafe实现了数组、HashMaps、TreeMaps等大型集合。为了避免/最小化碎片,我们使用dlmalloc over unsafe的概念实现了内存分配器。
这有助于我们获得并发性能。