sun.misc.Unsafe至少从2004年Java1.4开始就存在于Java中了。在Java9中,为了提高JVM的可维护性,Unsafe和许多其他的东西一起都被作为内部使用类隐藏起来了。但是究竟是什么取代Unsafe不得而知,个人推测会有不止一样来取代它,那么问题来了,到底为什么要使用Unsafe?
做一些Java语言不允许但是又十分有用的事情
很多低级语言中可用的技巧在Java中都是不被允许的。对大多数开发者而言这是件好事,既可以拯救你,也可以拯救你的同事们。同样也使得导入开源代码更容易了,因为你能掌握它们可以造成的最大的灾难上限。或者至少明确你可以不小心失误的界限。如果你尝试地足够努力,你也能造成损害。
那你可能会奇怪,为什么还要去尝试呢?当建立库时,Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法做同样的事情,即使它可能会更加危险同时也会失去Java的“一次编译,永久运行”的跨平台特性。
对象的反序列化
当使用框架反序列化或者构建对象时,会假设从已存在的对象中重建,你期望使用反射来调用类的设置函数,或者更准确一点是能直接设置内部字段甚至是final字段的函数。问题是你想创建一个对象的实例,但你实际上又不需要构造函数,因为它可能会使问题更加困难而且会有副作用。
public class A implements Serializable {
private final int num;
public A(int num) {
System.out.println("Hello Mum");
this.num = num;
}
public int getNum() {
return num;
}
}
在这个类中,应该能够重建和设置final字段,但如果你不得不调用构造函数时,它就可能做一些和反序列化无关的事情。有了这些原因,很多库使用Unsafe创建实例而不是调用构造函数。
Unsafe unsafe = getUnsafe();
Class aClass = A.class;
A a = (A) unsafe.allocateInstance(aClass);
调用allocateInstance函数避免了在我们不需要构造函数的时候却调用它。
线程安全的直接获取内存
Unsafe的另外一个用途是线程安全的获取非堆内存。ByteBuffer函数也能使你安全的获取非堆内存或是DirectMemory,但它不会提供任何线程安全的操作。你在进程间共享数据时使用Unsafe尤其有用。
import sun.misc.Unsafe; import sun.nio.ch.DirectBuffer; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class PingPongMapMain { public static void main(String... args) throws IOException { boolean odd; switch (args.length < 1 ? "usage" : args[0].toLowerCase()) { case "odd": odd = true; break; case "even": odd = false; break; default: System.err.println("Usage: java PingPongMain [odd|even]"); return; } int runs = 10000000; long start = 0; System.out.println("Waiting for the other odd/even"); File counters = new File(System.getProperty("java.io.tmpdir"), "counters.deleteme"); counters.deleteOnExit(); try (FileChannel fc = new RandomAccessFile(counters, "rw").getChannel()) { MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024); long address = ((DirectBuffer) mbb).address(); for (int i = -1; i < runs; i++) { for (; ; ) { long value = UNSAFE.getLongVolatile(null, address); boolean isOdd = (value & 1) != 0; if (isOdd != odd) // wait for the other side. continue; // make the change atomic, just in case there is more than one odd/even process if (UNSAFE.compareAndSwapLong(null, address, value, value + 1)) break; } if (i == 0) { System.out.println("Started"); start = System.nanoTime(); } } } System.out.printf("... Finished, average ping/pong took %,d ns%n", (System.nanoTime() - start) / runs); } static final Unsafe UNSAFE; static { try { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); UNSAFE = (Unsafe) theUnsafe.get(null); } catch (Exception e) { throw new AssertionError(e); } } }当你分别在两个程序,一个输入odd一个输入even,中运行时,可以看到两个进程都是通过持久化共享内存交换数据的。
在每个程序中,将相同的磁盘缓存映射到进程中。内存中实际上只有一份文件的副本存在。这意味着内存可以共享,前提是你使用线程安全的操作,比如volatile变量和CAS操作。(译注:CAS Compare and Swap 无锁算法)
在两个进程之间有83ns的往返时间。当考虑到System V IPC(进程间通信)大约需要2500ns,而且用IPC volatile替代persisted内存,算是相当快的了。
Unsafe适合在工作中使用吗?
个人不建议直接使用Unsafe。它远比原生的Java开发所需要的测试多。基于这个原因建议还是使用经过测试的库。如果你只是想自己用Unsafe,建议你最好在一个独立的类库中进行全面的测试。这限制了Unsafe在你的应用程序中的使用方式,但会给你一个更安全的Unsafe。
总结
Unsafe在Java中是很有趣的一个存在,你可以一个人在家里随便玩玩。它也有一些工作的应用程序特别是在写底层库的时候,但总的来说,使用经过测试的Unsafe库比直接用要好。
相关推荐
尽管UnSafe类并非设计为公共API的一部分,但它因其强大的功能而被广泛用于高性能的Java应用,如数据库系统、内存库和框架等。本文将深入探讨sun.misc.UnSafe类的源代码和主要功能。 一、UnSafe类概述 UnSafe类的...
总的来说,Java的Unsafe类虽然强大且高效,但其内部操作直接触及JVM的底层,使用不当可能会引发安全问题,因此在实际开发中应谨慎使用,通常只在对性能有极致追求或者需要自定义高级并发数据结构时考虑。理解并恰当...
Java开发中的堆外内存使用是优化程序性能的重要策略之一,尤其在处理大数据、高并发或者内存敏感的应用场景中。堆外内存,也被称为直接内存(Direct Memory),是指不在JVM的堆内存中分配的内存,而是直接在操作系统...
1. **使用Java API**:Java 9及以上版本引入了一个名为`sun.misc.Unsafe`的API,虽然它不是公开的,但可以通过反射访问。`Unsafe`类提供了一个`objectFieldOffset`方法,可以获取特定字段在对象内部的偏移量,从而...
C#引入了 unsafe 关键字,允许开发者在特定区域使用指针,但这需要开发者有更高的责任感。 内存管理方面,C#使用垃圾收集机制来自动回收不再使用的内存,这与Java相同。然而,C#引入了结构体(structs)和类...
然而,开发者有时会使用这些包来获取一些额外的功能或访问非公开的API。 1. **`sun.misc`**:这个包包含了各种实用工具和内部服务,如`Unsafe`类,它提供了对内存操作的低级访问,虽然不推荐使用,但在某些场景下...
首先,要使用Java客户端API访问Zookeeper,你需要引入相关的依赖。在Maven项目中,可以在pom.xml文件中添加以下依赖: ```xml <groupId>org.apache.zookeeper <artifactId>zookeeper <version>3.7.0 ``` 接...
我们了解到,虽然这些库为我们提供了便捷的JSON数据处理能力,但也可能因为不当使用而引入安全漏洞。因此,开发者在使用这些库时,需要了解其安全机制,严格控制数据来源,并及时关注和应用库的安全更新,以降低安全...
C#的Nullable类型允许空引用,而Java需要引入Java 8的Optional类来处理可能为空的情况。 5. **多线程**:Java 提供了Thread类和ExecutorService,C# 则有System.Threading命名空间,包含Task和ThreadPool。两者都...
C#的internal和protected internal提供了更细粒度的访问控制,分别对应于Java的包内访问和包内及派生类访问。Sealed类似于Java的final,阻止类被继承。 6. 方法参数: C#通过ref和out关键字支持按引用传递,而...
Java中的LockSupport是一个底层工具类,它在JDK 1.6版本中被引入到`java.util.concurrent.locks`包中,主要用于构建锁和其他同步工具类。LockSupport的核心功能是提供线程阻塞和唤醒的原语,这些功能在Java的并发...
由于直接操作内存,Unsafe类使得Java程序能够像C语言那样操作内存,从而绕过了Java语言的安全模型,因此使用不当可能会引入安全风险。 对于如何安全地获取Unsafe类的实例,文档中提到了两种方法。第一种是通过...
### Java技术分享知识点详解 #### 一、技术讨论 ##### 1.1 OFFHEAP -- 老宋分享 ...以上知识点覆盖了Java技术分享中的主要内容,包括技术讨论、体系架构及开源系统等方面,旨在为读者提供全面深入的理解。
- Java 8引入了红黑树来优化链表过长的情况,当链表长度超过8时,会转换为红黑树,以减少查找时间。 - 使用尾插法进行扩容,避免了Java 7的头插法可能导致的循环链表问题。 4. **ConcurrentHashMap的并发安全**:...
另外,Unsafe类是Java中一个特殊的类,它提供了硬件级别的原子操作和内存操作能力,但它是不安全的,不推荐直接使用。Unsafe类对于JDK内部的实现非常重要,但它在不同的JDK版本中可能会有所改变。 无锁算法还有许多...
3. **使用`invokeWithArguments()`**:Java 7引入了`invokeWithArguments()`方法,它比传统的`invoke()`方法更高效,因为它减少了参数转换的开销。 4. **避免数组作为参数**:反射调用时,如果参数是数组,尤其是...
本文将详细介绍`java.util.concurrent`包中的同步器框架的设计理念、实现机制、使用方法及性能表现,帮助开发者更好地理解和使用这一重要的并发工具。 #### 设计理念与实现 同步器框架的核心是`...
C# 和 Java 都被设计为使用动态调度的类似于 C++ 语法的完全面向对象的语言。这意味着这两种语言中的所有实体都是对象,并通过类和对象的概念来组织和管理程序逻辑。值得注意的是,虽然它们借鉴了 C++ 的语法风格,...
- **弃用不安全的API**:如`sun.misc.Unsafe`的部分方法被标记为弃用,鼓励开发者使用更安全的替代方案。 对于Windows 64位系统,安装JDK 11.0.10后,开发者可以设置环境变量`JAVA_HOME`指向安装目录,以便在命令行...