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类初探的更多相关文章

  1. 【实战Java高并发程序设计 1】Java中的指针:Unsafe类

    是<实战Java高并发程序设计>第4章的几点. 如果你对技术有着不折不挠的追求,应该还会特别在意incrementAndGet() 方法中compareAndSet()的实现.现在,就让我 ...

  2. Java sun.misc.Unsafe类的学习笔记

    Java未开源的Unsafe类 Unsafe类可以为我们提供高效并且线程安全方式操作变量,直接和内存数据打交道. 获取Unsafe实体的方法 private static Unsafe getUnsa ...

  3. java编程之:Unsafe类

    Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率.但是,它是一把双刃剑:正如它的名字所预示的那样,它是 Unsafe的,它所分配的内存需要 ...

  4. Java中Unsafe类详解

    http://www.cnblogs.com/mickole/articles/3757278.html Java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...

  5. 说一说Java的Unsafe类

    最近在看Java并发包的源码,发现了神奇的Unsafe类,仔细研究了一下,在这里跟大家分享一下. Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛 ...

  6. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  7. JDK 1.8 sun.misc.Unsafe类CAS底层实现

    在java.util.concurrent包下面的很多类为了追求性能都采用了sun.misc.Unsafe类中的CAS操作,从而避免使用synchronized等加锁方式带来性能上的不足. 在sun. ...

  8. Conccrent中 Unsafe类原理 以及 原子类AutomicXX的原理以及对Unsafe类的使用

    Unsafe类的介绍 Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类.请注意,JDK 1.8和之前JDK版本的中sun.misc.Un ...

  9. JDK中Unsafe类详解

    Java中Unsafe类详解 在openjdk8下看Unsafe源码 浅析Java中的原子操作 Java并发编程之LockSupport http://hg.openjdk.java.net/jdk7 ...

随机推荐

  1. PHP Callable强制指定回调类型的方法

    如果一个方法需要接受一个回调方法作为参数,我们可以这样写 <?php function dosth($callback){ call_user_func($callback); } functi ...

  2. thinkphp5 -- _initialize()初始化控制器

    public function _initialize() { parent::_initialize(); } public function __construct() { $; parent:: ...

  3. scrollview 和 listview滑动冲突解决

    http://blog.csdn.net/wanghao200906/article/details/51084975 http://www.cnblogs.com/shitianzeng/artic ...

  4. 2019-2020-1 20199310《Linux内核原理与分析》第三周作业

    1.问题描述 计算机的3大法宝是存储程序计算机,函数调用堆栈和中断机制,存储程序计算机已经在上一个博客中进行具体描述,本文将在剩下两方面出发对操作系统是如何工作的进行学习和探讨. 2.解决过程 2.1 ...

  5. JAVA企业级应用TOMCAT实战(三)

    JVM优化涉及到两大方面我个人的理解 .如何分配JVM的内存空间 .我应该使用什么垃圾回收器 JVM产生的垃圾需要回收.回收有不同的回收器. JVM的调优需要了解各个垃圾回收机制的原理. 终极目标:降 ...

  6. linux 之学习路线

    原文地址:https://www.oschina.net/question/587367_156024 推荐的发行版如下: UBUNTU 适合纯菜鸟,追求稳定的官方支持,对系统稳定性要求较弱,喜欢最新 ...

  7. atom 之 前端必备插件

    一. 语法支持 1. Language-label Ø ES2016.ESNext.JXS语法扩展 2. Language-postcss Ø Postcss语法高亮 二. 自动补全 1. Autoc ...

  8. Vue Cli 3 打包上线 部署到Apache Tomcat服务器

    使用 npm run build 打包项目 在根目录中有一个dist文件夹 我使用的服务器是 Apache  Tomcat 把项目放进tomcat /webapps 中 启动服务器 <mac O ...

  9. 07 模型层 orm相关查询 F查询Q查询 django开启事务

    一.Django终端打印SQL语句 如果你想知道你对数据库进行操作时,Django内部到底是怎么执行它的sql语句时可以加下面的配置来查看 在Django项目的settings.py文件中,在最后复制 ...

  10. Java——Java代码的执行顺序

    该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.构造方法 构造方 ...