文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2023-06-28 23:55

关注

这篇文章跟大家分析一下“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 alignmentlambda.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/AInstance size: 32 bytesSpace 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为addressunsafe.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为addressunsafe.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为addressunsafe.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类功能及使用注意事项是什么就分享到这里啦,希望上述内容能够让大家有所提升。如果想要学习更多知识,请大家多多留意小编的更新。谢谢大家关注一下编程网网站!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯