关于Unsafe类的一点研究

转载自  关于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类果然不安全!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326504.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2019蓝桥杯省赛---java---B---3(数列求值)

题目描述 思路分析 此题类似于斐波那契数列&#xff0c;但是所求20190324项的最后四位数字&#xff0c;要是单纯按照斐波那契数列的思想求下去&#xff0c; 别说long类型&#xff0c;BigInteger类型都存不了这么大的数&#xff0c;然后我们发现&#xff0c;所求20190324项的最…

Visual Studio 2017 ASP.NET Core开发

Visual Studio 2017 ASP.NET Core开发,Visual Studio 2017 已经内置ASP.NET Core 开发工具. 在选择.NET Core 功能安装以后就可以进行ASP.NET Core开发。 新的ASP.NET Core项目为csproj &#xff0c;打开之前的xproj项目&#xff0c;会提示单向升级&#xff0c;确认以后&#x…

2019蓝桥杯省赛---java---B---4(数的分解)

题目描述 思路分析 方案一&#xff1a;不定顺序&#xff1a;要除以6. 方案二&#xff1a;人为规定&#xff1a;a < b < c 代码实现 方案一 package com.atguigu.TEST;class Main{public static void main(String[] args) {int sum0;for (int i 1; i <1999; i) {f…

最通俗易懂的乐观锁与悲观锁原理及实现

转载自 最通俗易懂的乐观锁与悲观锁原理及实现 一、乐观锁 总是认为不会产生并发问题&#xff0c;每次去取数据的时候总认为不会有其他线程对数据进行修改&#xff0c;因此不会上锁&#xff0c;但是在更新时会判断其他线程在这之前有没有对数据进行修改&#xff0c;一般会使用…

mysql update field_mysql-更新表与另一个选择,但字段是SUM(someField)

基本上我有这样的事情&#xff1a;UPDATETableSETTable.col1 other_table.col1,FROMTableINNER JOINother_tableONTable.id other_table.id问题是我想用如下选择来更新col1&#xff1a;SELECT SUM(col1) FROM other_table WHERE Table.id other_table.id AND period > 20…

分布式一致性算法:Raft 算法

由于微信字数的限制&#xff0c;此处给出的并非全文&#xff0c;请拉到页面最下方点击阅读原文查看完整版。 Raft 算法是可以用来替代 Paxos 算法的分布式一致性算法&#xff0c;而且 raft 算法比 Paxos 算法更易懂且更容易实现。本文对 raft 论文进行翻译&#xff0c;希望能有…

聚集索引、辅助索引、覆盖索引、联合索引

转载自 聚集索引、辅助索引、覆盖索引、联合索引 聚集索引&#xff08;Clustered Index&#xff09; 聚集索引就是按照每张表的主键构造一棵B树&#xff0c;同时叶子节点中存放的即为整张表的行记录数据。 举个例子&#xff0c;直观感受下聚集索引。 创建表t&#xff0c;…

2019蓝桥杯省赛---java---B---6(特别数的和)

题目描述 时间限制: 1.0s 内存限制: 512.0MB 本题总分&#xff1a;15 分【问题描述】小明对数位中含有 2、0、1、9 的数字很感兴趣&#xff08;不包括前导 0&#xff09;&#xff0c;在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40&#xff0c;共 28 个&#xff0c;他…

java谓词_在Java中谓词

我假设你正在谈论来自Guava的com.google.common.base.Predicate 。从API&#xff1a;确定给定input的true值或false值。 例如&#xff0c;一个RegexPredicate可能实现了Predicate &#xff0c;并且对任何匹配给定正则expression式的string返回true。这本质上是一个booleantesti…

.NET Core项目部署到linux(Centos7)

1.开篇说明 a 上篇博客简单的说明了一下 使用.NET Core开发的一个总结&#xff08;.NET Core跨平台&#xff1a;使用.NET Core开发一个初心源商城总括)&#xff0c;那么这篇博客我们就在上一篇博客的基础上对其代码进行部署&#xff0c;将其部署在Linux Centos7下。 b 这周周二…

2020蓝桥杯省赛---java---B---1(指数计算)

题目描述 代码实现 解法一 计算器 解法二 package com.atguigu.lanqiao;import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner input new Scanner(System.in);int a1,b1921,c7;for (int i 0; i < 2020; i) {aa*7;if(a>1…

java安装显示更改_java 安装与配置

到java.sun.com下载自解压安装包jdk-1_5_0_06-nb-4_1-linux-ml.bin安装&#xff1a;[rootRHEL4 JDK-1.5.0.06]# chmod x jdk-1_5_0_06-nb-4_1-linux-ml.bin[rootRHEL4 JDK-1.5.0.06]# ./jdk-1_5_0_06-nb-4_1-linux-ml.bin选择安装位置&#xff0c;NetBeans安装目录&#xff1a;…

dayjs​​​​​​​文档

转载自 dayjs API Dayjs 并没有改变或覆盖 Javascript 原生的 Date.prototype&#xff0c; 而是创造了一个全新的包含 Javascript Date 对象的 Dayjs的对象。 Dayjs 对象是不可变的, 所有的 API 操作都将返回一个新的 Dayjs 对象。 解析 当前时间时间字符串Unix 时间戳 (…

ASP.NET Core 优雅的在开发环境保存机密(User Secrets)

前言 在应用程序开发的过程中&#xff0c;有的时候需要在代码中保存一些机密的信息&#xff0c;比如加密密钥&#xff0c;字符串&#xff0c;或者是用户名密码等。通常的做法是保存到一个配置文件中&#xff0c;在以前我们会把他保存到web.config中&#xff0c;但是在ASP.NET C…

震惊,中国历朝历代疆域变迁视频【高清】

中国历代疆域指的是从古至今中国领土变化过程的历史&#xff0c;期间经过数千年的发展历程。 中国疆域自远古以来不断演进变化&#xff0c;从《尚书禹贡》九州开始直到唐朝极盛时疆域:被认为是中国疆域原型。至元朝时不仅统一中国&#xff0c;西藏地方从此正式纳入中国中央政府…

2020蓝桥杯省赛---java---B---2(指数计算)

题目描述 【问题描述】 小明设计了一种文章加密的方法&#xff1a;对于每个字母 c&#xff0c;将它变成某个另外的字符 Tc。下表给出了字符变换的规则&#xff1a; 例如&#xff0c;将字符串 YeRi 加密可得字符串 EaFn。 小明有一个随机的字符串&#xff0c;加密后为 EaF…

java项目新东方在线源码_[VIP源码]【S019】SSM框架开发智夫子在线考试系统项目源码 百度云盘...

java源码项目名称&#xff1a;SSM框架开发智夫子在线考试系统项目源码ssm项目源码5 y& C [0 w! J% F1 n8 z J百度网盘下载链接&#xff1a;4 B i* }. G4 }8 |7 l% G8 游客&#xff0c;如果您要查看本帖隐藏内容请->>回复[/hide]_* r Q0 _! W1 K4 Q" U6 F密码: …

关于Dapper.NET的相关论述

年少时&#xff0c;为何不为自己的梦想去拼搏一次呢&#xff1f;纵使头破血流&#xff0c;也不悔有那年少轻狂。感慨很多&#xff0c;最近事情也很多&#xff0c;博客也很少更新了&#xff0c;毕竟每个人都需要为自己的生活去努力。 最近在一个群里遇到一个人说的话&#xff0c…

81. 搜索旋转排序数组 II---Leecode----java

思路分析 旋转前后&#xff0c;数组中的值总是不变&#xff0c;可以直接进行比较。 代码实现 class Solution {public boolean search(int[] nums, int target) {for(int i0;i<nums.length;i){if(nums[i]target){return true;}}return false;} }

数年之前的寒假

版权声明&#xff1a;文章可以随便转载&#xff0c;但是转载时带上原文地址来源&#xff0c;侵权必究 https://blog.csdn.net/qq_34137397/article/details/54882702对于上学的孩子来说每年都有寒假&#xff0c;可是一样的寒假总是不一样的玩法。自从高中毕业之后&#xff0c;很…