Unsafe

从这个类的名字Unsafe上来说这个类就是一个不安全的类,也是不开放给用户直接使用的(当然我们还是可以通过其他一些方法用到)。
这个类在jdk源码中多个类中用到,主要作用是任意内存地址位置处读写数据,外加一下CAS操作。它的大部分操作都是绕过JVM通过JNI完成的,因此它所分配的内存需要手动free,所以是非常危险的。但是Unsafe中很多(但不是所有)方法都很有用,且有些情况下,除了使用JNI,没有其他方法弄够完成同样的事情。ConcurrentHashMap 和 原子操作CAS等都有用到。

//下面是sun.misc.Unsafe.java类源码
package sun.misc;
import java.lang.reflect.Field;

/*这个类提供了一个更底层的操作并且应该在受信任的代码中使用。可以通过内存地址
存取fields,如果给出的内存地址是无效的那么会有一个不确定的运行表现。*/

public class Unsafe
{
  private static final sun.misc.Unsafe theUnsafe;    //通过反射获取它可以获得Unsafe实例
  private Unsafe(){}
   /*获得Unsafe实例,但是这个方法如果我们写的时候调用会报错,因为这个方法在调用时会判断类加载器,
我们的代码是没有“受信任”的,而在jdk源码中调用是没有任何问题的;*/
  public static sun.misc.Unsafe getUnsafe() { }
/*
   * 返回指定field的在类中内存地址偏移量值,这个值对于给定的field是唯一的,并且后续对该方法的调用都应该返回相同的值。  
   * @param field 需要返回偏移量的field,一般通过class反射Object.class.getDeclaredField("field")获取该类的字段field
   * @return 指定字段field的偏移量*/
  public native long objectFieldOffset(Field field);
/*
   * 在obj的offset位置中的field值和期望的值比较,如果相同则将update值设置到offset位置中的
   * field。这个方法的操作应该是原子的,因此提供了一种不可中断的方式更新field。
   * @param obj 包含要修改field的对象
   * @param offset obj中整型field的偏移量
   * @param expect 希望field中存在的值
   * @param update 如果期望值expect与field的当前值相同,设置filed的值为update值
   * @return 如果field的值被更改则true,否则为false*/
  public native boolean compareAndSwapInt(Object obj, long offset,int expect, int update);
/*
   * 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义
   * @param obj 包含需要修改field的对象
   * @param offset obj中long型field的偏移量
   * @param value field将被设置的新值*/
  public native void putOrderedLong(Object obj, long offset, long value);
/*
   * 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义
   * @param obj 包含需要修改field的对象
   * @param offset obj中long型field的偏移量
   * @param value field将被设置的新值
   * @see #putLong(Object,long,long)*/
  public native void putLongVolatile(Object obj, long offset, long value);
/*
   * 设置obj对象中offset偏移地址对应的long型field的值为指定值。
   * @param 包含需要修改field的对象
   * @param obj中long型field的偏移量
   * @param value field将被设置的新值
   * @see #putLongVolatile(Object,long,long) */
  public native void putLong(Object obj, long offset, long value);
/*
   * 获取obj对象中offset偏移地址对应的long型field的值,支持volatile load语义。 
   * @param obj  包含需要去读取的field的对象
   * @param offset obj中long型field的偏移量
   * @see #getLong(Object,long)*/
  public native long getLongVolatile(Object obj, long offset);
/*
   * 获取obj对象中offset偏移地址对应的long型field的值
   * @param obj 包含需要去读取的field的对象
   * @param offset obj中long型field的偏移量
   * @see #getLongVolatile(Object,long)*/
  public native long getLong(Object obj, long offset);
/*
   * 获取给定数组中第一个元素的偏移地址。
   * 为了存取数组中的元素,这个偏移地址与arrayIndexScale方法的非0返回值一起被使用。
   * @param arrayClass 第一个元素地址被获取的class
   * @return 数组第一个元素 的偏移地址
   * @see arrayIndexScale(Class)*/
  public native int arrayBaseOffset(Class arrayClass);
/*
   * 获取用户给定数组寻址的增量地址.一个合适的换算因子不能返回的时候(例如:基本类型),
   * 返回0.这个返回值能够与arrayBaseOffset一起使用去存取这个数组class中的元素
   * @param 应该被返回的数组类
   * @return 返回增量地址, 如果结束返回0
   */
  public native int arrayIndexScale(Class arrayClass);  
/*
   * 释放被park(挂起)创建的在一个线程上的阻塞.这个
   * 方法也可以被使用来终止一个先前调用park导致的阻塞.
   * 这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。
   * @param thread 要解除阻塞的线程*/
  public native void unpark(Thread thread);
/*
   * 阻塞一个线程直到unpark出现、线程
   * 被中断或者timeout时间到期。如果一个unpark调用已经出现了,
   * 这里只计数。timeout为0表示永不过期.当isAbsolute为true时,
   * timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时
   * 也可能不合理地返回(没有具体原因)
   * @param isAbsolute 如果为true timeout的值是一个相对于新纪元之后的毫秒数
   * @param time 可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到到达这个时间点
   */
  public native void park(boolean isAbsolute, long time);
}

当然里面方法很多都是重复的还有很多不仅常用的,这里只写一部分,还有内存相关的

public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);

类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。Unsafe的直接内存访问:用Unsafe开辟的内存空间不占用Heap空间,当然也不具有自动内存回收功能。做到像C一样自由利用系统内存资源。应用的话看这篇

2、下面是对它的总结:

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

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

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

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

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

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

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

2.1Class相关

//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
public native long objectFieldOffset(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)及对象引用类型都有以下方法。

//获得对象的字段偏移量
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);
//创建对象,但并不会调用其构造方法。如果类未被初始化,将初始化类。
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

2.3数组相关

//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
public static final int ARRAY_BOOLEAN_BASE_OFFSET
 = theUnsafe.arrayBaseOffset(boolean[].class);

//返回数组中每一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);
//boolean、byte、short、char、int、long、float、double,及对象类型均有以下方法
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。

//更新变量值为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;
}

2.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);

2.4.3volatile相关读写

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

public native void putOrderedObject(Object o, long offset, Object x);
public native void putOrderedInt(Object o, long offset, int x);
public native void putOrderedLong(Object o, long offset, long x);

2.4.4内存屏障相关

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

//内存屏障,禁止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回收)

//(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系统相关

//返回指针的大小。返回值为4或8。
public native int addressSize();  
public static final int ADDRESS_SIZE = theUnsafe.addressSize();  
//内存页的大小。
public native int pageSize();

 

### 功能概述 Java 中的 `Unsafe` 类是一个非公开类,位于 `sun.misc` 包下,提供了底层操作功能。这些功能包括直接操作内存、执行原子操作以及执行其他低级别任务。`Unsafe` 类的主要作用是绕过 Java 虚拟机(JVM)的某些限制,以实现高性能的操作。例如,它可以直接分配和释放内存,而不需要依赖 JVM 的垃圾回收机制[^1]。 ### 核心功能 1. **内存操作** `Unsafe` 类允许直接分配和释放内存,这在处理大块数据时特别有用。例如,可以使用 `allocateMemory` 方法分配一块内存,并使用 `freeMemory` 方法手动释放这块内存。这种操作方式类似于 C 语言中的 `malloc` 和 `free`[^3]。 2. **原子操作** `Unsafe` 类支持原子操作,这对于实现无锁数据结构非常重要。例如,`compareAndSwapInt` 方法可以用于实现原子更新操作,这在并发编程中非常常见。Java 的 `java.util.concurrent` 包中的许多类(如 `AtomicInteger`)内部依赖于 `Unsafe` 类来实现高效的原子操作。 3. **对象操作** `Unsafe` 类还允许直接操作对象的字段。例如,可以通过 `objectFieldOffset` 方法获取对象字段的偏移量,然后使用 `getInt` 和 `putInt` 等方法直接读取和修改对象的字段值。这种方式可以绕过 Java 的访问控制,直接操作对象的内部状态。 4. **线程调度** `Unsafe` 类提供了一些线程调度相关的功能,例如 `park` 和 `unpark` 方法。这些方法可以用于实现线程的阻塞和唤醒操作,是 `java.util.concurrent.locks.LockSupport` 类的基础[^2]。 ### 使用方法 #### 获取 `Unsafe` 实例 由于 `Unsafe` 类的构造函数是私有的,并且没有提供公共的静态方法来获取实例,因此需要通过反射来获取 `Unsafe` 实例。以下是一个示例代码: ```java private static Unsafe getUnsafeInstance() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } return null; } ``` #### 内存分配与释放 一旦获取了 `Unsafe` 实例,就可以使用它来分配和释放内存。以下是一个简单的示例,展示了如何分配一块内存并进行初始化和释放: ```java private static final int BUFFER_SIZE = 1024; private static final Unsafe unsafe = getUnsafeInstance(); private static final long buffer = unsafe.allocateMemory(BUFFER_SIZE); public static void main(String[] args) { // 初始化内存 unsafe.setMemory(buffer, BUFFER_SIZE, (byte) 0); // ... 进行其他操作 // 释放内存 unsafe.freeMemory(buffer); } ``` #### 原子操作示例 以下是一个使用 `Unsafe` 类实现原子操作的示例。假设有一个整数变量 `value`,我们希望对其进行原子更新: ```java private volatile int value; private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset( MyAtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } public boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } ``` ### 注意事项 尽管 `Unsafe` 类提供了强大的底层操作功能,但其使用也伴随着一定的风险。首先,`Unsafe` 类的操作绕过了 JVM 的许多安全机制,因此可能导致程序崩溃或数据损坏。其次,由于 `Unsafe` 类的操作不被 JVM 管理,因此需要手动管理内存,这增加了内存泄漏的风险。最后,`Unsafe` 类并不是 Java 标准的一部分,因此在不同的 JVM 实现中可能会有不同的行为。因此,除非确实需要高性能的操作,否则不建议直接使用 `Unsafe` 类[^2]。 ### 相关问题 1. `Unsafe` 类在 Java 中有哪些常见的应用场景? 2. 如何通过 `Unsafe` 类实现高效的原子操作? 3. 使用 `Unsafe` 类进行内存操作时需要注意哪些问题? 4. `Unsafe` 类在不同 JVM 实现中的兼容性如何? 5. 有哪些替代 `Unsafe` 类的方案可以实现类似的功能?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值