Unsafe类初探
Unsafe类是java中非常特别的一个类。它名字就叫做“不安全”,提供的操作可以直接读写内存、获得地址偏移值、锁定或释放线程。
通过正常途径是无法获得Unsafe实例的,首先它的构造方法是私有的,然后,即使你调用它的getUnsafe方法,也会抛出SecurityException。
A collection of methods for performing low-level, unsafe operations. Although the class and all methods are public, use of this class islimited because only trusted code can obtain instances of it.
任何关于Unsafe类的文章都不会推荐我们在代码中使用它,但这并不妨碍我们了解它可以做什么。下面我们来看下利用Unsafe类我们是否可以做点有趣的事情。
获取Unsafe实例
public static Unsafe getUnsafeInstance() throws Exception{
Field unsafeStaticField =
Unsafe.class.getDeclaredField("theUnsafe");
unsafeStaticField.setAccessible(true);
return (Unsafe) unsafeStaticField.get(Unsafe.class);
}
通过java反射机制,我们跳过了安全检测,拿到了一个unsafe类的实例。
我找遍了Unsafe类的API,没有发现可以直接获取对象地址的方法,Unsafe中操作地址相关的方法都要求提供一个Object类型的参数,用来获取对象的初始地址。
修改和读取数组中的值
Unsafe u = getUnsafeInstance();
int[] arr = {1,2,3,4,5,6,7,8,9,10};
int b = u.arrayBaseOffset(int[].class);
int s = u.arrayIndexScale(int[].class);
u.putInt(arr, (long)b+s*9, 1);
for(int i=0;i<10;i++){
int v = u.getInt(arr, (long)b+s*i);
System.out.print(v+“ ”);
}
打印结果:1 2 3 4 5 6 7 8 9 1 ,可以看到,成功读出了数组中的值,而且最后一个值由10改为了1。
- arrayBaseOffset: 返回当前数组第一个元素地址相对于数组起始地址的偏移值,在本例中返回6。
- arrayIndexScale: 返回当前数组一个元素占用的字节数,在本例中返回4。
- putInt(obj,offset,intval): 获取数组对象obj的起始地址,加上偏移值,得到对应元素的地址,将intval写入内存。
- getInt(obj,offset): 获取数组对象obj的起始地址,加上偏移值,得到对应元素的地址,从而获得元素的值。
- 偏移值: 数组元素偏移值 = arrayBaseOffset + arrayIndexScalse * i。
获取对象实例
/** 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;
allocateInstance: 在不执行构造方法的前提下,获取一个类的实例,即使这个类的构造方法是私有的。
修改静态变量和实例变量的值
先定义一个Test类
public class Test {
public int intfield ;
public static int staticIntField;
public static int[] arr;
private Test(){
System.out.println("constructor called");
}
}
修改Test类的实例变量
Unsafe u = getUnsafeInstance();
Test t = (Test) u.allocateInstance(Test.class);
long b1 = u.objectFieldOffset(Test.class.getDeclaredField("intfield"));
u.putInt(t, b1, 2);
System.out.println("intfield:"+t.intfield);
这里使用allocateInstance方法获取了一个Test类的实例,并且没有打印“constructor called”,说明构造方法没有调用。
修改实例变量与修改数组的值类似,同样要获取地址偏移值,然后调用putInt方法。
- objectFieldOffset: 获取对象某个属性的地址偏移值。
我们通过Unsafe类修改了Java堆中的数据。
修改Test类的静态变量
Field staticIntField = Test.class.getDeclaredField("staticIntField");
Object o = u.staticFieldBase(staticIntField);
System.out.prinln(o==Test.class);
Long b4 = u.staticFieldOffset(staticIntField);
//因为是静态变量,传入的Object参数应为class对象
u.putInt(o, b4, 10);
System.out.println("staticIntField:"+u.getInt(Test.class, b4));
打印结果:
true
staticIntField:10
静态变量与实例变量不同之处在于,静态变量位于于方法区中,它的地址偏移值与Test类在方法区的地址相关,与Test类的实例无关。
- staticFieldBase: 获取静态变量所属的类在方法区的首地址。可以看到,返回的对象就是Test.class。
- staticFieldOffset: 获取静态变量地址偏移值。
我们通过Unsafe类修改了方法区中的信息。
调戏String.intern
在jdk7中,String.intern不再拷贝string对象实例,而是保存第一次出现的对象的引用。在下面的代码中,通过Unsafe修改被引用对象s的私有属性value达到间接修改s1的效果!
String s = "abc";
//保存s的引用
s.intern();
//此时s1==s,地址相同
String s1 = "abc";
Unsafe u = getUnsafeInstance();
//获取s的实例变量value
Field valueInString = String.class.getDeclaredField("value");
//获取value的变量偏移值
long offset = u.objectFieldOffset(valueInString);
//value本身是一个char[],要修改它元素的值,仍要获取baseOffset和indexScale
long base = u.arrayBaseOffset(char[].class);
long scale = u.arrayIndexScale(char[].class);
//获取value
char[] values = (char[]) u.getObject(s, offset);
//为value赋值
u.putChar(values, base + scale, 'c');
System.out.println("s:"+s+" s1:"+s1);
//将s的值改为 abc
s = "abc";
String s2 = "abc";
String s3 = "abc";
System.out.println("s:"+s+" s1:"+s1);
打印结果:
s:acc s1:acc
s:acc s1:acc s2:acc s3:acc
我们发现了什么?所有值为“abc”的字符串都变成了“acc”!!!
Unsafe类果然不安全!!!
版权声明:本文为博主原创文章,未经博主允许不得转载.
Unsafe类初探的更多相关文章
- 【实战Java高并发程序设计 1】Java中的指针:Unsafe类
是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...
- Java sun.misc.Unsafe类的学习笔记
Java未开源的Unsafe类 Unsafe类可以为我们提供高效并且线程安全方式操作变量,直接和内存数据打交道. 获取Unsafe实体的方法 private static Unsafe getUnsa ...
- java编程之:Unsafe类
Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率.但是,它是一把双刃剑:正如它的名字所预示的那样,它是 Unsafe的,它所分配的内存需要 ...
- Java中Unsafe类详解
http://www.cnblogs.com/mickole/articles/3757278.html Java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...
- 说一说Java的Unsafe类
最近在看Java并发包的源码,发现了神奇的Unsafe类,仔细研究了一下,在这里跟大家分享一下. Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛 ...
- Java中的Unsafe类111
1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...
- JDK 1.8 sun.misc.Unsafe类CAS底层实现
在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足. 在sun. ...
- Conccrent中 Unsafe类原理 以及 原子类AutomicXX的原理以及对Unsafe类的使用
Unsafe类的介绍 Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类.请注意,JDK 1.8和之前JDK版本的中sun.misc.Un ...
- JDK中Unsafe类详解
Java中Unsafe类详解 在openjdk8下看Unsafe源码 浅析Java中的原子操作 Java并发编程之LockSupport http://hg.openjdk.java.net/jdk7 ...
随机推荐
- 手把手教你使用Python爬取西刺代理数据(下篇)
/1 前言/ 前几天小编发布了手把手教你使用Python爬取西次代理数据(上篇),木有赶上车的小伙伴,可以戳进去看看.今天小编带大家进行网页结构的分析以及网页数据的提取,具体步骤如下. /2 首页分析 ...
- Test Test...
标题: Test(一级标题) Test(二级标题) Test(三级标题) 列表: test(列表) Alpha Beta Gamma test 2 Delte Epsilon 链接: 点兔成金斐波那契 ...
- tagbar 调到函数定义再跳回
首先要在源码文件夹下执行 ctags -R * 生成tags文件 齐次要安装 YouCompleteMe ctrl + ] 跳到函数定义 Ctrl-o 和 Ctrl-I 跳回.我试验的只有 Ctrl- ...
- Spring Boot JPA中关联表的使用
文章目录 添加依赖 构建Entity 构建Repository 构建初始数据 测试 Spring Boot JPA中关联表的使用 本文中,我们会将会通过一个Book和Category的关联关系,来讲解 ...
- Libra教程之:数据结构和存储
文章目录 存储的数据结构 账本历史 账本状态 账户 事件 前面的文章我们知道,libra会把所有的数据都存储在账本中.为了方便业务逻辑和数据的校验,这个存储是以特定的数据结构来实现的,这里我们叫做验证 ...
- 徐州A
#include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;++i) #defi ...
- Eclipse Mac OS版 卸载svn插件subclipse
点击Eclipse -> About Eclipse 在打开的窗口中点击Installation Details(安装细节) 在Installed Software标签窗口中,选中Subclip ...
- IDC:企业需求疲软 第三季度全球服务器市场收入下滑7%
根据IDC全球服务器季度追踪报告,2016年第三季度全球服务器市场同比减少7%至125亿美元.整个服务器市场的增长最近有所放缓,部分原因是超大规模数据中心增长放缓,以及受到高端服务器销售下滑的拖累.此 ...
- du命令、df命令、磁盘分区
df:汇报文件系统的磁盘使用空间[root@localhost ~]# df文件系统 1K-块 已用 可用 已用% 挂载点/dev/sda3 29140072 1022920 28117152 4% ...
- 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析
开始学习<LInux网络编程>中的综合案例,虽然代码书上有,还是自己打一下加深理解和印象. 主要有两个函数,完成命令行的解析,另一个实现配置文件的解析,注释还是比较丰富的哦. //star ...