反射、Unsafe、直接调用性能大比拼
在java动态表达式、原生调用、反射性能对比这一篇的基础上,本文我们将比较原生调用和直接通过Unsafe指针操作java对象的性能。
package com.hundsun.jrescloud; import sun.misc.Unsafe; import java.lang.reflect.Field; /** * @author zjhua * @description * @date 2020/1/26 20:20 */ public class TestUnSafe { static final Unsafe unsafe; static final long stateOffset; static final long volStateOffset; private volatile long volState = 0; private long state = 0; static { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); # 对于不在rt.jar下的类,只能通过反射获取Unsafe实例,否则会发生安全越界 field.setAccessible(true); unsafe = (Unsafe) field.get(null); stateOffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("state")); volStateOffset = unsafe.objectFieldOffset(TestUnSafe.class.getDeclaredField("volState")); } catch (Exception ex) { System.out.println(ex.getLocalizedMessage()); throw new Error(ex); } } public static void main(String[] args) { TestUnSafe test = new TestUnSafe(); long beg = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { unsafe.putLong(test, stateOffset,i); } System.out.println("unsafe put non volatile: " + (System.currentTimeMillis() - beg)); beg = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { test.state = i; } System.out.println("pojo put non volatile: " + (System.currentTimeMillis() - beg)); beg = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { unsafe.putLong(test, stateOffset,i); } System.out.println("unsafe put volatile: " + (System.currentTimeMillis() - beg)); beg = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { test.volState = i; } System.out.println("pojo put volatile: " + (System.currentTimeMillis() - beg)); } }
输出如下:
unsafe put non volatile: 179 // 普通属性,直接操作更快。如果使用setter的话,则有时候unsafe快,有时候pojo快。按说unsafe直接通过指针操作,至少不应该更慢才对,所以唯一的原因应该是方法调用本身的损耗。 pojo put non volatile: 68 unsafe put volatile: 114 // volatile属性,unsafe要快近6倍。 pojo put volatile: 795
对于字符串操作,上述结论亦成立,所有的JUC包中的原子类AtomicXXX都是通过Unsafe实现的,其底层的各compareAndSwapXXX方法都是JNI实现。
除了get/put外,Unsafe类还包含很多CAS(cas作为一种优化方式,其代价是cpu利用率在冲突高的时候很厉害,此时锁更合理)功能(JUC很多原子和CAS就是基于Unsafe实现)、park、unpark功能,LockSupport工具类就是调用的Unsafe的park与unpark实现的。
注:Reflection.getCallerClass(n)已经被废弃,不应该再使用,它主要用来跟踪哪些类调用了本方法,不过Reflection.getCallerClass()仍然可用,在跟踪工具类的时候时还是比较有用的。
关于Unsafe更全面的使用介绍,可以参见https://my.oschina.net/editorial-story/blog/3019773。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2017-01-26 c++继承、多态以及与java的行为差异之处
2017-01-26 c++标准库多线程入门
2017-01-26 c++中的字符集与中文(宽字符)
2017-01-26 c++随机数及rand()的缺陷