sun unsafe类功能及使用注意事项是什么

发布时间:2022-01-26 11:51:43 作者:柒染
来源:亿速云 阅读:145

这篇文章跟大家分析一下“sun unsafe类功能及使用注意事项是什么”。内容详细易懂,对“sun unsafe类功能及使用注意事项是什么”感兴趣的朋友可以跟着小编的思路慢慢深入来阅读一下,希望阅读后能够对大家有所帮助。下面跟着小编一起深入学习“sun unsafe类功能及使用注意事项是什么”的知识吧。

Unsafe简介

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。

但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。

在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

获取Unsafe实例

private static sun.misc.Unsafe getUnsafe() {
        try {
            return AccessController.doPrivileged(new PrivilegedExceptionAction<Unsafe>() {
                @Override
                public sun.misc.Unsafe run() throws Exception {
                    Class<sun.misc.Unsafe> k = sun.misc.Unsafe.class;
                    for (Field f : k.getDeclaredFields()) {
                        f.setAccessible(true);
                        Object x = f.get(null);
                        if (k.isInstance(x)) {
                            return k.cast(x);
                        }
                    }
                    // The sun.misc.Unsafe field does not exist.
                    throw new Error("unsafe is null");
                }
            });
        } catch (Throwable e) {
            throw new Error("get unsafe failed", e);
        }
    }

Unsafe功能列表

Unsafe的数组操作

unsafe中,有两个关于数组的方法:

public native int arrayBaseOffset(Class<?> arrayClass);
public native int arrayIndexScale(Class<?> arrayClass);

base offset含义

首先,在Java中,数组也是对象

In the Java programming language, arrays are objects (&sect;4.3.1), are dynamically created, and may be assigned to variables of type Object (&sect;4.3.2). All methods of class Object may be invoked on an array.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-10.html

那么既然是对象,就会有object header,占用一部分空间,那么理解数组的base offset也就不难了

比如下面的一段JOL输出,实际上对象的属性数据是从OFFSET 16的位置开始的,0-12的空间被header所占用

HotSpot 64-bit VM, COOPS, 8-byte alignment
lambda.Book object internals:

 OFFSET  SIZE           TYPE DESCRIPTION                               VALUE
      0    12                (object header)                           N/A
     12     4            int Book.sales                                N/A
     16     4         String Book.title                                N/A
     20     4      LocalDate Book.publishTime                          N/A
     24     4         String Book.author                               N/A
     28     4   List<String> Book.tags                                 N/A
Instance size: 32 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

那么如果要访问对象的属性数据,需要基于基地址(base address)进行偏移,基地址+基础偏移(base offset)+属性偏移(field offset)才是数据的内存地址(逻辑),那么title属性的内存地址实际上就是:

book(instance).title field address = book object base address + base offset + title field offset

数组在Java里可以视为一种特殊的对象,无论什么类型的数组,他们在内存中都会有一分基础偏移的空间,和object header类似

经过测试,64位的JVM数组类型的基础偏移都是16(测试结果在不同JVM下可能会有所区别)
原始类型的基础偏移都是12(测试结果在不同JVM下可能会有所区别)
可以使用JOL工具查看原始类型包装类的offset,比如int就看Integer的,虽然jdk没有提供函数,但是通过JOL也是可以获取的,jvm类型和对其方式匹配即可

index scale含义

就是指数组中每个元素所占用的空间大小,比如int[] scale就是4,long[] scale就是8,object[] scale就是4(指针大小)

有了这个offset,就可以对数组进行copyMemory操作了

public native void copyMemory(Object srcBase, long srcOffset,
                                  Object destBase, long destOffset,
                                  long bytes);

array copy to direct memory

在使用copyMemory操作时,需要传入对象及对象的base offset,对于数组来说,offset就是上面介绍的offset,比如现在将一个数组中的数据拷贝至DirectBuffer

byte[] byte = new byte[4096];
unsafe.copyMemory(byte,ARRAY_BYTE_BASE_OFFSET,null,directAddr,4096);

之所以使用unsafe而不是ByteBuffer的方法来操作DirectBuffer,是因为ByteBuffer不够灵活。

比如我想把一个byte[]拷贝至DirectBuffer的某个位置中,就没有相应的方法;只能先设置position,然后再put(byte[], int, int),非常麻烦,而且并发访问时position(long)和put也是个非原子性操作

但是用unsafe来操作的话就很轻松了,直接copyMemory,直接指定address + offset就行

unsafe.copyMemory(byte,ARRAY_BYTE_BASE_OFFSET,null,directAddr,4096);

copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes)

通过copyMemory方法,可以做各种拷贝操作:

对象(一般是数组)拷贝到指定堆外内存地址

long l = unsafe.allocateMemory(1);
data2[0] = 5;
//目标是memory address,destBase为null,destOffset为address
unsafe.copyMemory(data2,16,null,l,1);

将对象拷贝到对象

byte[] data1 = new byte[1];
data1[0] = 9;

byte[] data2 = new byte[1];
unsafe.copyMemory(data1,16,data2,16,1);

将堆外内存地址的数据拷贝到堆内(一般是数组)

byte[] data2 = new byte[1];

long l = unsafe.allocateMemory(1);
unsafe.putByte(l, (byte) 2);
//源数据是memory address,srcBase为null,srcOffset为address
unsafe.copyMemory(null,l,data2,16,1);

堆外内存地址互相拷贝

long l = unsafe.allocateMemory(1);
long l2 = unsafe.allocateMemory(1);
unsafe.putByte(l, (byte) 2);
//源数据是memory address,srcBase为null,srcOffset为address
//目标是memory address,destBase为null,destOffset为address
unsafe.copyMemory(null,l,null,l2,1);

Benchmark

sun.misc.Unsafe#putInt(java.lang.Object, long, int) & object field manual set

禁用JIT结果:

BenchmarkModeCntScoreErrorUnits
ObjectFieldSetBenchmark.manualSetthrpt28646455.472 ops/ns
ObjectFieldSetBenchmark.unsafeSetthrpt27901066.170 ops/ns

启用JIT结果:

BenchmarkModeCntScoreErrorUnits
ObjectFieldSetBenchmark.manualSetthrpt2477232013.545 ops/ns
ObjectFieldSetBenchmark.unsafeSetthrpt2499135982.962 ops/ns

结论,几乎没区别

什么时候用Unsafe

一般使用DirectBuffer时,需要配合Unsafe来使用,因为DirectBuffer的内存是分配在JVM Heap之外的,属于C Heap,所以需要用直接操作内存地址(逻辑),和C里malloc之后的操作方式一样

关于sun unsafe类功能及使用注意事项是什么就分享到这里啦,希望上述内容能够让大家有所提升。如果想要学习更多知识,请大家多多留意小编的更新。谢谢大家关注一下亿速云网站!

推荐阅读:
  1. Java并发编程Unsafe类的源码分析以及Unsafe类的使用方法
  2. Java中有哪些Unsafe类

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

sun unsafe

上一篇:MYSQL数据库怎么设置主从同步

下一篇:@Transactional注解怎么用

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》