sun.misc.Unsafe的理解
前言
以下sun.misc.Unsafe源码和demo基于jdk1.7;
最近在看J.U.C里的源码,很多都用到了sun.misc.Unsafe这个类,一知半解,看起来总感觉有点不尽兴,所以打算对Unsafe的源码及使用做个分析;
另外,网上找了份c++的源代码natUnsafe.cc(可惜比较老,Copyright (C) 2006, 2007年的,没找到新的),也就是sun.misc.Unsafe的C++实现,跟Unsafe类中的native方法对照起来看更加容易理解;
Unsafe类的作用
可以用来在任意内存地址位置处读写数据,可见,对于普通用户来说,使用起来还是比较危险的;
另外,还支持一些CAS原子操作;
获取Unsafe对象
遗憾的是,Unsafe对象不能直接通过new Unsafe()
或调用Unsafe.getUnsafe()
获取,原因如下:
*不能直接new Unsafe()
,原因是Unsafe
被设计成单例模式,构造方法是私有的;
*不能通过调用Unsafe.getUnsafe()获取,因为
getUnsafe被设计成只能从引导类加载器(bootstrap class loader)加载,从getUnsafe
的源码中也可以看出来,如下:
@CallerSensitivepublic static Unsafe getUnsafe() {//得到调用该方法的Class对象Class cc = Reflection.getCallerClass();//判断调用该方法的类是否是引导类加载器(bootstrap class loader)//如果不是的话,比如由AppClassLoader调用该方法,则抛出SecurityException异常if (cc.getClassLoader() != null)throw new SecurityException("Unsafe");//返回单例对象return theUnsafe;}
虽然我们不能通过以上方法得到Unsafe对象,但得Unsafe类中有个私有的静态全局属性theUnsafe(Unsafe实例对象)
,通过反射,可以获取到该成员属性theUnsafe对应的Field对象,并将其设置为可访问,从而得到theUnsafe具体对象,如下代码:
package concurrency;import java.lang.reflect.Field; import sun.misc.Unsafe; import sun.reflect.Reflection;public class Test {public static void main(String[] args) throws NoSuchFieldException,SecurityException, IllegalArgumentException, IllegalAccessException {// 通过反射得到theUnsafe对应的Field对象Field field = Unsafe.class.getDeclaredField("theUnsafe");// 设置该Field为可访问field.setAccessible(true);// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的Unsafe unsafe = (Unsafe) field.get(null);System.out.println(unsafe);} }
Unsafe类中的API
allocateInstance方法,不调用构造方法生成对象
本地方法,功能是生成一个对象实例,但是不会运行该对象的构造方法;由于natUnsafe.cc版本较老,没找到对应的c++实现;
/** Allocate an instance but do not run any constructor. Initializes the class if it has not yet been. */public native Object allocateInstance(Class cls)throws InstantiationException;
例子,利用Unsafe的allocateInstance方法,在未调用构造方法的情况下生成了对象:
package concurrency;import java.lang.reflect.Field;import sun.misc.Unsafe; import sun.reflect.Reflection;class User {private String name = "";private int age = 0;public User() {this.name = "test";this.age = 22;}@Overridepublic String toString() {return name + ": " + age;} }public class Test {public static void main(String[] args) throws NoSuchFieldException,SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {// 通过反射得到theUnsafe对应的Field对象Field field = Unsafe.class.getDeclaredField("theUnsafe");// 设置该Field为可访问field.setAccessible(true);// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的Unsafe unsafe = (Unsafe) field.get(null);User user = (User) unsafe.allocateInstance(User.class);System.out.println(user); //dont invoke constructor, print null: 0 User userFromNormal = new User();System.out.println(userFromNormal); //print test: 22 } }
objectFieldOffset方法,返回成员属性在内存中的地址相对于对象内存地址的偏移量
比较简单,就是返回成员属性内存地址相对于对象内存地址的偏移量,通过该方法可以计算一个对象在内存中的空间大小,方法是通过反射得到它的所有Field(包括父类继承得到的),找出Field中偏移量最大值,然后对该最大偏移值填充字节数即为对象大小;
关于该方法的使用例子可以看下面的修改内存数据的例子;
putLong,putInt,putDouble,putChar,putObject等方法,直接修改内存数据(可以越过访问权限)
这里,还有put对应的get方法,很简单就是直接读取内存地址处的数据,不做举例;
我们可以举个putLong(Object, long, long)方法详细看下其具体实现,其它的类似,先看Java的源码,没啥好看的,就声明了一个native本地方法:
三个参数说明下:
Object o//对象引用
long offset//对象内存地址的偏移量
long x//写入的数据
public native void putLong(Object o, long offset, long x);
还是看下natUnsafe.cc中的c++实现吧,很简单,就是计算要写入数据的内存地址,然后写入数据,如下:
void sun::misc::Unsafe::putLong (jobject obj, jlong offset, jlong value) {jlong *addr = (jlong *) ((char *) obj + offset);//计算要修改的数据的内存地址=对象地址+成员属性地址偏移量spinlock lock;//自旋锁,通过循环来获取锁, i386处理器需要加锁访问64位数据,如果是int,则不需要改行代码*addr = value;//往该内存地址位置直接写入数据 }
如下例子,即使User类的成员属性是私有的且没有提供对外的public方法,我们还是可以直接在它们的内存地址位置处写入数据,并成功;
package concurrency;import java.lang.reflect.Field;import sun.misc.Unsafe; import sun.reflect.Reflection;class User {private String name = "test"; private long id = 1;private int age = 2;private double height = 1.72;@Overridepublic String toString() {return name + "," + id + "," + age + "," + height;} }public class Test {public static void main(String[] args) throws NoSuchFieldException,SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {// 通过反射得到theUnsafe对应的Field对象Field field = Unsafe.class.getDeclaredField("theUnsafe");// 设置该Field为可访问field.setAccessible(true);// 通过Field得到该Field对应的具体对象,传入null是因为该Field为static的Unsafe unsafe = (Unsafe) field.get(null);User user = new User();System.out.println(user); //打印test,1,2,1.72 Class userClass = user.getClass();Field name = userClass.getDeclaredField("name");Field id = userClass.getDeclaredField("id");Field age = userClass.getDeclaredField("age");Field height = userClass.getDeclaredField("height");//直接往内存地址写数据unsafe.putObject(user, unsafe.objectFieldOffset(name), "midified-name");unsafe.putLong(user, unsafe.objectFieldOffset(id),100l);unsafe.putInt(user, unsafe.objectFieldOffset(age), 101);unsafe.putDouble(user, unsafe.objectFieldOffset(height), 100.1);System.out.println(user);//打印midified-name,100,101,100.1 } }
copyMemory、freeMemory
copyMemory:内存数据拷贝
freeMemory:用于释放allocateMemory和reallocateMemory申请的内存
CAS操作的方法,compareAndSwapInt,compareAndSwapLong等
看下natUnsafe.cc中的c++实现吧,加深理解,其实就是将内存值与预期值作比较,判断是否相等,相等的话,写入数据,不相等不做操作,返回旧数据;
static inline bool compareAndSwap (volatile jint *addr, jint old, jint new_val) {jboolean result = false;spinlock lock;if ((result = (*addr == old)))*addr = new_val;return result; }
J.U.C里原子类就是基于以上CAS操作实现的;
getLongVolatile/putLongVolatile等等方法
这类方法使用volatile语义去存取数据,我的理解就是各个线程不缓存数据,直接在内存中读取数据;
参考连接:
https://github.com/aeste/gcc/blob/master/libjava/sun/misc/natUnsafe.cc
http://ifeve.com/sun-misc-unsafe/
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/misc/Unsafe.java
转载于:https://www.cnblogs.com/chenpi/p/5389254.html
sun.misc.Unsafe的理解相关推荐
- 深入理解sun.misc.Unsafe原理
前言 Unsafe类在JDK源码中被广泛使用,在Spark使用off-heap memory时也会使用到,该类功能很强大,涉及到类加载机制(深入理解ClassLoader工作机制),其实例一般情况是获 ...
- openJDK之sun.misc.Unsafe类CAS底层实现
2019独角兽企业重金招聘Python工程师标准>>> 注:这篇文章参考了https://www.cnblogs.com/snowater/p/8303698.html,而后自己结合 ...
- sun.misc.Unsafe操作手册
欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...
- 聊聊高并发(十七)解析java.util.concurrent各个组件(一) 了解sun.misc.Unsafe类
了解了并发编程中锁的基本原理之后,接下来看看Java是如何利用这些原理来实现各种锁,原子变量,同步组件的.在开始分析java.util.concurrent的源代码直接,首先要了解的就是sun.mis ...
- sun.misc.Unsafe苦难告诉我们什么
Oracle将删除Java 9中的内部sun.misc.Unsafe类 . 尽管大多数人可能对这种变化漠不关心,但其他一些人(主要是图书馆开发人员)并非如此. 博客圈中最近有几篇文章描绘了这种变化所暗 ...
- 一文了解sun.misc.Unsafe
Java语言和JVM平台已经度过了20岁的生日.它最初起源于机顶盒.移动设备和Java-Card,同时也应用在了各种服务器系统中,Java已成为物联网(Internet of Things)的通用语言 ...
- sun.misc.Unsafe类详解
一.Unsafe类的作用 可以用来在任意内存地址位置处读写数据(可见,对于普通用户来说,使用起来还是比较危险的): 支持一些CAS原子操作: 二.获取Unsafe对象 以下sun.misc.Unsaf ...
- 【Java报错】记录一次 sun.misc.Unsafe.park(Native Method) Conflicting setter definitions for property 导致的内存泄露
1. 报错信息 2021-10-29 08:44:56 WARN [,,,] [main] o.a.c.loader.WebappClassLoaderBase - The web applicati ...
- 字体对话框java实验_Java Web安全 || Java基础 sun.misc.Unsafe
点击上方"凌天实验室","星标或置顶公众号" 漏洞.技术还是其他,我都想第一时间和你分享 " [历史]已连载更新全部内容:[菜单栏]-[JAVA SE ...
最新文章
- Python-time
- 大众EA211djs和css的区别,宣传上却说捷达VS5用的发动机是EA211,但这款车的参数配置栏发动机型号写的却是DJS,谁能为我解惑吗?(只有斯柯达的柯米克的发动机型号写得才是EA211)...
- vim命令杂烩(复制粘贴、建文件、撤销等)
- Centos7.6环境Docker安装Oracle19c企业版
- MFCWinInet学习
- 解决【npm ERR! Unexpected end of JSON input while parsing near '...sh_time:141072930277'】方案...
- kux格式怎么转换成mp3_kux格式怎么转换成mp4?快速转换格式的方法
- 夜神模拟器只有android5怎么办,夜神安卓模拟器很卡怎么办 夜神模拟器好卡解决方法...
- java并发--活动对象
- java计算机毕业设计springboot+vue城市轨道交通线路查询系统-公交车线路查询
- 硬盘柱面损坏怎么办_电脑硬盘坏了怎么办?不花一分钱就能成功修复!
- 为了革命 保护眼睛 !—— 眼科专家配置的色调
- 这6款软件,让你的工作效率提升90%!
- gitlab artifacts too large
- 橡皮擦的英语_2019年成人高考考试,语文数学英语政治想得高分答题技巧看这里...
- Nginx1.6.2版本安装遇到的坑以及配置反向代理
- 给20块钱买可乐,每瓶可乐3块钱,喝完之后退瓶子可以换回1块钱,问最多可以喝到多少瓶可乐?
- 以下 D 不是MYSQL的对象权限_数据库课后习题及答案
- 言语理解与表达之逻辑填空
- 小程序学习-购物车及商品清单功能实现
热门文章
- ES6中关于set数据结构详解
- 管理数据通用权限系统快速开发框架设计
- MySQL中如何定义外键[转]
- 后序遍历(非递归)☆
- linux系统下安装和配置redis(2021版)
- springboot整合redis,推荐整合和使用案例(2021版)
- 如何删除pdf中计算机属性,PDF文件做好后怎么修改
- js 添加属性_轻松理解JS中的面向对象,顺便搞懂prototype和__proto__
- 如何计算机操作维护培训,电脑基本操作培训教材.ppt
- 最易懂的Spring IOC原理讲解