一篇看懂Java中的Unsafe类

时间:2021-10-20 18:07:45

前言

本文主要给大家介绍了关于java中unsafe类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

1.unsafe类介绍

unsafe类是在sun.misc包下,不属于java标准。但是很多java的基础类库,包括一些被广泛使用的高性能开发库都是基于unsafe类开发的,比如netty、hadoop、kafka等。

使用unsafe可用来直接访问系统内存资源并进行自主管理,unsafe类在提升java运行效率,增强java语言底层操作能力方面起了很大的作用。

unsafe可认为是java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等。

 官方并不建议使用unsafe。

下面是使用unsafe的一些例子。

1.1实例化私有类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.lang.reflect.field;
import sun.misc.unsafe;
public class unsafeplayer {
 public static void main(string[] args) throws exception {
 //通过反射实例化unsafe
 field f = unsafe.class.getdeclaredfield("theunsafe");
 f.setaccessible(true);
 unsafe unsafe = (unsafe) f.get(null);
 //实例化player
 player player = (player) unsafe.allocateinstance(player.class);
 player.setname("li lei");
 system.out.println(player.getname());
 }
}
 
class player{
 private string name;
 private player(){}
 public string getname() {
 return name;
 }
 public void setname(string name) {
 this.name = name;
 }
}

1.2cas操作,通过内存偏移地址修改变量值

java并发包中的synchronousqueue中的transferstack中使用cas更新栈顶。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/ unsafe mechanics
private static final sun.misc.unsafe unsafe;
private static final long headoffset;
static {
 try {
 unsafe = sun.misc.unsafe.getunsafe();
 class<?> k = transferstack.class;
 headoffset = unsafe.objectfieldoffset
  (k.getdeclaredfield("head"));
 } catch (exception e) {
 throw new error(e);
 }
}
//栈顶
volatile snode head;
//更新栈顶
boolean cashead(snode h, snode nh) {
 return h == head &&
 unsafe.compareandswapobject(this, headoffset, h, nh);
}

1.3直接内存访问

unsafe的直接内存访问:用unsafe开辟的内存空间不占用heap空间,当然也不具有自动内存回收功能。做到像c一样*利用系统内存资源。

2.unsafe类源码分析

unsafe的大部分api都是native的方法,主要包括以下几类:

1)class相关。主要提供class和它的静态字段的操作方法。

2)object相关。主要提供object和它的字段的操作方法。

3)arrray相关。主要提供数组及其中元素的操作方法。

4)并发相关。主要提供低级别同步原语,如cas、线程调度、volatile、内存屏障等。

5)memory相关。提供了直接内存访问方法(绕过java堆直接操作本地内存),可做到像c一样*利用系统内存资源。

6)系统相关。主要返回某些低级别的内存信息,如地址大小、内存页大小。

2.1class相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//静态属性的偏移量,用于在对应的class对象中读写静态属性
public native long staticfieldoffset(field f);
 
public native object staticfieldbase(field f);
//判断是否需要初始化一个类
public native boolean shouldbeinitialized(class<?> c);
//确保类被初始化
public native void ensureclassinitialized(class<?> c);
//定义一个类,可用于动态创建类
public native class<?> defineclass(string name, byte[] b, int off, int len,
     classloader loader,
     protectiondomain protectiondomain);
//定义一个匿名类,可用于动态创建类
public native class<?> defineanonymousclass(class<?> hostclass, byte[] data, object[] cppatches);

2.2object相关

java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。

?
1
2
3
4
5
6
//获得对象的字段偏移量
public native long objectfieldoffset(field f);
//获得给定对象地址偏移量的int值
public native int getint(object o, long offset);
//设置给定对象地址偏移量的int值
public native void putint(object o, long offset, int x);
?
1
2
3
//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
public native object allocateinstance(class<?> cls)
 throws instantiationexception;

2.3数组相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
 * report the offset of the first element in the storage allocation of a
 * given array class. if {@link #arrayindexscale} returns a non-zero value
 * for the same class, you may use that scale factor, together with this
 * base offset, to form new offsets to access elements of arrays of the
 * given class.
 *
 * @see #getint(object, long)
 * @see #putint(object, long, int)
 */
//返回数组中第一个元素的偏移地址
public native int arraybaseoffset(class<?> arrayclass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** the value of {@code arraybaseoffset(boolean[].class)} */
public static final int array_boolean_base_offset
 = theunsafe.arraybaseoffset(boolean[].class);
 
/**
 * report the scale factor for addressing elements in the storage
 * allocation of a given array class. however, arrays of "narrow" types
 * will generally not work properly with accessors like {@link
 * #getbyte(object, int)}, so the scale factor for such classes is reported
 * as zero.
 *
 * @see #arraybaseoffset
 * @see #getint(object, long)
 * @see #putint(object, long, int)
 */
//返回数组中每一个元素占用的大小
public native int arrayindexscale(class<?> arrayclass);
 
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
/** the value of {@code arrayindexscale(boolean[].class)} */
public static final int array_boolean_index_scale
 = theunsafe.arrayindexscale(boolean[].class);

通过arraybaseoffset和arrayindexscale可定位数组中每个元素在内存中的位置。

2.4并发相关

 2.4.1cas相关

cas:compareandswap,内存偏移地址offset,预期值expected,新值x。如果变量在当前时刻的值和预期值expected相等,尝试将变量的值更新为x。如果更新成功,返回true;否则,返回false。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//更新变量值为x,如果当前值为expected
//o:对象 offset:偏移量 expected:期望值 x:新值
public final native boolean compareandswapobject(object o, long offset,
       object expected,
       object x);
 
public final native boolean compareandswapint(object o, long offset,
      int expected,
      int x);
 
public final native boolean compareandswaplong(object o, long offset,
      long expected,
      long x);

从java 8开始,unsafe中提供了以下方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//增加
public final int getandaddint(object o, long offset, int delta) {
 int v;
 do {
 v = getintvolatile(o, offset);
 } while (!compareandswapint(o, offset, v, v + delta));
 return v;
}
 
public final long getandaddlong(object o, long offset, long delta) {
 long v;
 do {
 v = getlongvolatile(o, offset);
 } while (!compareandswaplong(o, offset, v, v + delta));
 return v;
}
//设置
public final int getandsetint(object o, long offset, int newvalue) {
 int v;
 do {
 v = getintvolatile(o, offset);
 } while (!compareandswapint(o, offset, v, newvalue));
 return v;
}
 
public final long getandsetlong(object o, long offset, long newvalue) {
 long v;
 do {
 v = getlongvolatile(o, offset);
 } while (!compareandswaplong(o, offset, v, newvalue));
 return v;
}
 
public final object getandsetobject(object o, long offset, object newvalue) {
 object v;
 do {
 v = getobjectvolatile(o, offset);
 } while (!compareandswapobject(o, offset, v, newvalue));
 return v;

2.4.2线程调度相关

?
1
2
3
4
5
6
7
8
9
10
//取消阻塞线程
public native void unpark(object thread);
//阻塞线程
public native void park(boolean isabsolute, long time);
//获得对象锁
public native void monitorenter(object o);
//释放对象锁
public native void monitorexit(object o);
//尝试获取对象锁,返回true或false表示是否获取成功
public native boolean trymonitorenter(object o);

2.4.3volatile相关读写

java中的基本类型(boolean、byte、char、short、int、long、float、double)及对象引用类型都有以下方法。

?
1
2
3
4
5
6
7
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
//相当于getobject(object, long)的volatile版本
public native object getobjectvolatile(object o, long offset);
 
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
//相当于putobject(object, long, object)的volatile版本
public native void putobjectvolatile(object o, long offset, object x);
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * version of {@link #putobjectvolatile(object, long, object)}
 * that does not guarantee immediate visibility of the store to
 * other threads. this method is generally only useful if the
 * underlying field is a java volatile (or if an array cell, one
 * that is otherwise only accessed using volatile accesses).
 */
public native void putorderedobject(object o, long offset, object x);
 
/** ordered/lazy version of {@link #putintvolatile(object, long, int)} */
public native void putorderedint(object o, long offset, int x);
 
/** ordered/lazy version of {@link #putlongvolatile(object, long, long)} */
public native void putorderedlong(object o, long offset, long x);

2.4.4内存屏障相关

java 8引入 ,用于定义内存屏障,避免代码重排序。

?
1
2
3
4
5
6
//内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadfence();
//内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storefence();
//内存屏障,禁止load、store操作重排序
public native void fullfence();

2.5直接内存访问(非堆内存)

allocatememory所分配的内存需要手动free(不被gc回收)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//(boolean、byte、char、short、int、long、float、double)都有以下get、put两个方法。
//获得给定地址上的int值
public native int getint(long address);
//设置给定地址上的int值
public native void putint(long address, int x);
//获得本地指针
public native long getaddress(long address);
//存储本地指针到给定的内存地址
public native void putaddress(long address, long x);
 
//分配内存
public native long allocatememory(long bytes);
//重新分配内存
public native long reallocatememory(long address, long bytes);
//初始化内存内容
public native void setmemory(object o, long offset, long bytes, byte value);
//初始化内存内容
public void setmemory(long address, long bytes, byte value) {
 setmemory(null, address, bytes, value);
}
//内存内容拷贝
public native void copymemory(object srcbase, long srcoffset,
    object destbase, long destoffset,
    long bytes);
//内存内容拷贝
public void copymemory(long srcaddress, long destaddress, long bytes) {
 copymemory(null, srcaddress, null, destaddress, bytes);
}
//释放内存
public native void freememory(long address);

2.6系统相关

?
1
2
3
4
5
6
7
8
//返回指针的大小。返回值为4或8。
public native int addresssize();
 
/** the value of {@code addresssize()} */
public static final int address_size = theunsafe.addresssize();
 
//内存页的大小。
public native int pagesize();

3.参考资料

 说一说java中的unsafe类

java魔法类:sun.misc.unsafe

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:https://www.cnblogs.com/yeyang/p/9074894.html