简单聊一聊Java中的Unsafe类


在阅读AtomicInteger的源码时,看到了这个类:sum.msic.Unsafe,之前从没见过。所以花了点时间研究了下,下面这篇文章主要给大家介绍了关于Java中Unsafe类的相关资料。

Unsafe类介绍

Unsafe类是在sun.misc包下,不属于Java标准。如图所示:

在这里插入图片描述

但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty、Hadoop、Kafka等。

使用Unsafe可用来直接访问系统内存资源并进行自主管理,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用。但是正如它的名字所预示的那样,它是Unsafe的,它所分配的内存需要手动free(不被GC回收)。

Unsafe可认为是Java中留下的后门,提供了一些低层次操作,如直接内存访问、线程调度等;官方并不建议使用Unsafe。

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

1. 实例化私有类

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; 
 } 
}

2. CAS操作,通过内存偏移地址修改变量值

java并发包中的SynchronousQueue中的TransferStack中使用CAS更新栈顶。

// 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);
}

3. 直接内存访问

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

Unsafe类源码分析

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

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

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

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

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

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

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

1. Class相关

// 静态属性的偏移量,用于在对应的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. Object相关

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

// 获得对象的字段偏移量 
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);

3. 数组相关

/**
 * 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可定位数组中每个元素在内存中的位置。

4. 并发相关

4.1 CAS相关

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

// 更新变量值为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中提供了以下方法:

// 增加
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;

4.2 线程调度相关

// 取消阻塞线程
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);

4.3 volatile相关读写

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

// 从对象的指定偏移量处获取变量的引用,使用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);

/**
 * 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);

4.4 内存屏障相关

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

// 内存屏障,禁止load操作重排序,即屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
// 内存屏障,禁止store操作重排序,即屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
// 内存屏障,禁止load、store操作重排序
public native void fullFence();

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

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

//(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);

4.6 系统相关

// 返回指针的大小。返回值为4或8。
public native int addressSize();
 
/** The value of {@code addressSize()} */
public static final int ADDRESS_SIZE = theUnsafe.addressSize();
 
// 内存页的大小。
public native int pageSize();

猜你喜欢

转载自blog.csdn.net/Greenarrow961224/article/details/113247750