简要介绍:
Java语言先比较与C和C++有一个非常大的不同点在于Java语言无法直接操作内存,实际开发中,默认都是由JVM来进行内存分配和垃圾回收,而JVM在进行垃圾回收的时候,绝大多数垃圾回收器都需要STW(stop the world)这个问题往往会导致服务短暂或者较长时间的暂停。因此Unsafe提供了通过Java直接操作内存的API,尽管Unsafe是JavaNIO和并发的核心类,但是其如其名,这是一个官方不推荐开发者使用的及其不安全的类!
主要作用:
一、获取Unsafe
源码-基于jdk1.8
二、操作方法
数组操作对象操作
package com.liziba.unsafe; import com.liziba.unsafe.pojo.User; import sun.misc.Unsafe; import java.io.File; import java.io.FileInputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; /** * <p> * 操作对象示例 * </p> * * @Author: Liziba * @Date: 2021/5/24 20:40 */ public class OperateObjectExample { /** * 1、public native Object allocateInstance(Class<?> var1); 分配内存 * 2、public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); 方法定义一个类用于动态的创建类 * @throws Exception */ public static void operateObjectUseUnsafe() throws Exception{ Unsafe unsafe = UnsafeFactory.getUnsafe(); // 使用Unsafe的allocateInstance()方法,可以无需使用构造函数的情况下实例化对象 User user = (User) unsafe.allocateInstance(User.class); user.setId(1); user.setName("李子捌"); System.out.println(user); // 返回对象成员属性在内存中相对于对象在内存中地址的偏移量 Field name = User.class.getDeclaredField("name"); long fieldOffset = unsafe.objectFieldOffset(name); // 使用Unsafe的putXxx()方法,可以直接修改内存地址指向的数据(可以越过权限访问控制符) unsafe.putObject(user, fieldOffset, "李子柒"); System.out.println(user); // 使用Unsafe在运行时通过.class文件,创建类 File classFile = new File("E:\\workspaceall\\liziba-javap5\\out\\production\\liziba-javap5\\com\\liziba\\unsafe\\pojo\\User.class"); FileInputStream fis = new FileInputStream(classFile); byte [] classContent = new byte[(int) classFile.length()]; fis.read(classContent); Class<?> clazz = unsafe.defineClass(null, classContent, 0, classContent.length, null, null); Constructor<?> constructor = clazz.getDeclaredConstructor(int.class, String.class); System.out.println(constructor.newInstance(1, "李子玖")); } public static void main(String[] args) { try { OperateObjectExample.operateObjectUseUnsafe(); } catch (Exception e) { e.printStackTrace(); } } }
输出结果
内存操作
package com.liziba.unsafe; import sun.misc.Unsafe; /** * <p> * 內存地址操作示例 * </p> * * @Author: Liziba * @Date: 2021/5/24 21:32 */ public class OperateMemoryExample { /** * 1、public native long allocateMemory(long var1); 分配var1字节大小的内存,返回起始地址偏移量 * 2、public native long reallocateMemory(long var1, long var3); 重新给var1起始地址的内存分配长度为var3字节的内存,返回新的内存起始地址偏移量 * 3、public native void freeMemory(long var1); 释放起始地址为var1的地址 * * 分配地址的方法还有重分配,都是分配在堆外内存,返回的是一个long类型的地址偏移量。这个偏移量在Java程序中的每一块内存都是唯一的 * */ public static void operateMemoryUseUnsafe() { Unsafe unsafe = UnsafeFactory.getUnsafe(); // 申请分配8byte的内存 long address = unsafe.allocateMemory(1L); // 初始化内存填充值 unsafe.putByte(address, (byte)1); // 测试输出 System.out.println(new StringBuilder().append("address: ").append(address).append(" byte value: ").append(unsafe.getByte(address))); // 重新分配一个地址 long newAddress = unsafe.reallocateMemory(address, 8L); unsafe.putLong(newAddress, 8888L); System.out.println(new StringBuilder().append("address: ").append(newAddress).append(" long value: ").append(unsafe.getLong(newAddress))); // 释放地址,注意地址可能被其他使用 unsafe.freeMemory(newAddress); System.out.println(new StringBuilder().append("address: ").append(newAddress).append(" long value: ").append(unsafe.getLong(newAddress))); } public static void main(String[] args) { OperateMemoryExample.operateMemoryUseUnsafe(); } }
CAS操作
package com.liziba.unsafe; import com.liziba.unsafe.pojo.User; import sun.misc.Unsafe; import java.lang.reflect.Field; /** * <p> * CAS操作示例 * </p> * * @Author: Liziba * @Date: 2021/5/24 22:18 */ public class OperateCASExample { /** * CAS == compare and swap(比较并替换) * 当需要改变的值为期望值的时候,就替换为新的值,是原子(不可再分割)操作。Java中大量的并发框架底层使用到了CAS操作。 * 优势:无锁操作,减少线程切换带来的开销 * 缺点:CAS容易在并发的情况下失败从而引发性能问题,也存在ABA问题。 * * Unsafe中提供了三个方法 * 1、compareAndSwapInt * 2、compareAndSwapLong * 3、compareAndSwapObject * */ public static void operateCASUseUnsafe() throws Exception { User user = new User(1, "李子捌"); System.out.println("pre user value: " + user); Unsafe unsafe = UnsafeFactory.getUnsafe(); Field id = user.getClass().getDeclaredField("id"); Field name = user.getClass().getDeclaredField("name"); // 获取ID字段的内存偏移量 long idFieldOffset = unsafe.objectFieldOffset(id); // 获取name字段的内存偏移量 long nameFieldOffset = unsafe.objectFieldOffset(name); // 如果ID的期望值是1,则修改为18 success unsafe.compareAndSwapInt(user, idFieldOffset, 1, 18); // 如果name的期望值是小荔枝,则修改为李子柒 fail unsafe.compareAndSwapObject(user, nameFieldOffset, "小荔枝", "李子柒"); // 输出修改的user对象 System.out.println("post user value: " + user); } public static void main(String[] args) { try { OperateCASExample.operateCASUseUnsafe(); } catch (Exception e) { e.printStackTrace(); } } }
/** * 查看Java的java.util.concurrent.locks.LockSupport源代码可以发现LockSupport类 * 中有各种版本的pack方法但是最终都是通过调用Unsafe.park()方法实现的。 */ public class LockSupport { public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); } public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); } public static void parkNanos(Object blocker, long nanos) { if (nanos > 0) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, nanos); setBlocker(t, null); } } public static void parkNanos(long nanos) { if (nanos > 0) UNSAFE.park(false, nanos); } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(true, deadline); setBlocker(t, null); } public static void parkUntil(long deadline) { UNSAFE.park(true, deadline); } }