Java Unsafe类实现原理及测试代码

作者:bf378 时间:2023-03-10 21:18:13 

Unsafe类介绍

第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe?读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能。

Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有。Oracle正在计划从Java 9中去掉Unsafe类,如果真是如此影响就太大了。

Unsafe类提供了以下这些功能:

一、内存管理。包括分配内存、释放内存等。

该部分包括了allocateMemory(分配内存)、reallocateMemory(重新分配内存)、copyMemory(拷贝内存)、freeMemory(释放内存 )、getAddress(获取内存地址)、addressSize、pageSize、getInt(获取内存地址指向的整数)、getIntVolatile(获取内存地址指向的整数,并支持volatile语义)、putInt(将整数写入指定内存地址)、putIntVolatile(将整数写入指定内存地址,并支持volatile语义)、putOrderedInt(将整数写入指定内存地址、有序或者有延迟的方法)等方法。getXXX和putXXX包含了各种基本类型的操作。

利用copyMemory方法,我们可以实现一个通用的对象拷贝方法,无需再对每一个对象都实现clone方法,当然这通用的方法只能做到对象浅拷贝。

二、非常规的对象实例化。

allocateInstance()方法提供了另一种创建实例的途径。通常我们可以用new或者反射来实例化对象,使用allocateInstance()方法可以直接生成对象实例,且无需调用构造方法和其它初始化方法。

这在对象反序列化的时候会很有用,能够重建和设置final字段,而不需要调用构造方法。

三、操作类、对象、变量。

这部分包括了staticFieldOffset(静态域偏移)、defineClass(定义类)、defineAnonymousClass(定义匿名类)、ensureClassInitialized(确保类初始化)、objectFieldOffset(对象域偏移)等方法。

通过这些方法我们可以获取对象的指针,通过对指针进行偏移,我们不仅可以直接修改指针指向的数据(即使它们是私有的),甚至可以找到JVM已经认定为垃圾、可以进行回收的对象。

四、数组操作。

这部分包括了arrayBaseOffset(获取数组第一个元素的偏移地址)、arrayIndexScale(获取数组中元素的增量地址)等方法。arrayBaseOffset与arrayIndexScale配合起来使用,就可以定位数组中每个元素在内存中的位置。

由于Java的数组最大值为Integer.MAX_VALUE,使用Unsafe类的内存分配方法可以实现超大数组。实际上这样的数据就可以认为是C数组,因此需要注意在合适的时间释放内存。

五、多线程同步。包括锁机制、CAS操作等。

这部分包括了monitorEnter、tryMonitorEnter、monitorExit、compareAndSwapInt、compareAndSwap等方法。

其中monitorEnter、tryMonitorEnter、monitorExit已经被标记为deprecated,不建议使用。

Unsafe类的CAS操作可能是用的最多的,它为Java的锁机制提供了一种新的解决办法,比如AtomicInteger等类都是通过该方法来实现的。compareAndSwap方法是原子的,可以避免繁重的锁机制,提高代码效率。这是一种乐观锁,通常认为在大部分情况下不出现竞态条件,如果操作失败,会不断重试直到成功。

六、挂起与恢复。

这部分包括了park、unpark等方法。

将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。

七、内存屏障。

这部分包括了loadFence、storeFence、fullFence等方法。这是在Java 8新引入的,用于定义内存屏障,避免代码重排序。

loadFence() 表示该方法之前的所有load操作在内存屏障之前完成。同理storeFence()表示该方法之前的所有store操作在内存屏障之前完成。fullFence()表示该方法之前的所有load、store操作在内存屏障之前完成。

测试代码


import com.User;
import org.junit.Before;
import org.junit.Test;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;

class User {

public static String USER_CLASS_NAME = "User.class";
 private int age;
 private String name;

public int getAge() {
   return age;
 }

public String getName() {
   return name;
 }

public User(int age, String name) {
   this.age = age;
   this.name = name;
 }

public void setAge(int age) {
   this.age = age;
 }

public void setName(String name) {
   this.name = name;
 }
}

public class LockTests {

Unsafe unSafe;
 User u = new User(17, "zhangsan");

@Before
 public void before() throws Exception {
   Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
   theUnsafeField.setAccessible(true);
   unSafe = (Unsafe) theUnsafeField.get(Unsafe.class);
 }

@Test
 public void objectFieldOffset() throws Exception {
   // unSafe偏底层的一个Java工具类
   java.util.List users = new ArrayList();
   for (int i = 0; i < 10; i++) {
     Field ageField = User.class.getDeclaredField("age");
     User u = new User(18, "daxin");
     users.add(u);
     //使用内存获取User age字段在内存中的 offset
     // 是相对地址,不是一个绝对地址
     long ageOffset = unSafe.objectFieldOffset(ageField);
     // 每次都相同
     System.out.println("ageOffset = " + ageOffset);
   }
 }

@Test
 public void compareAndSwapInt() throws Exception {

// unSafe偏底层的一个Java工具类
   Field ageField = User.class.getDeclaredField("age");

User u = new User(18, "daxin");

//使用内存获取User age字段在内存中的 offset
   long ageOffset = unSafe.objectFieldOffset(ageField);

// 修改之前的值
   System.out.println(u.getAge());
   // 进行CAS更新, 由于设置18 因此CAS 会成功
   unSafe.compareAndSwapInt(u, ageOffset, 18, 20);
   System.out.println(u.getAge());

// 由于age设置20 进行CAS失败
   unSafe.compareAndSwapInt(u, ageOffset, 18, 22);
   System.out.println(u.getAge());

}

@Test
 public void ensureClassInitialized() {
   System.out.println("==== start ====");
   unSafe.ensureClassInitialized(ClassIsLoad.class);
   // 再次 确认不会报错
   unSafe.ensureClassInitialized(ClassIsLoad.class);
 }

/**
  * AQS 底层的Node链表就是基于这个工具实现的 。
  *
  * @throws Exception
  */
 @Test
 public void getValueByFieldOffset() throws Exception {
   for (int i = 0; i < 10; i++) {
     User u = new User(18, UUID.randomUUID().toString().substring(i, 20));
     int age = unSafe.getInt(u, 12L);
     System.out.println("age = " + age);

// 获取名字 field offset
     Field nameField = User.class.getDeclaredField("name");
     long nameOffset = unSafe.objectFieldOffset(nameField);
     System.out.println("nameOffset = " + nameOffset);
     String name = unSafe.getObject(u, nameOffset) + "";
     System.out.println("name = " + name);
   }
 }

@Test
 public void pageSize() {
   System.out.println("unSafe.pageSize() = " + unSafe.pageSize());
 }

/**
  * AtomicInteger 底层是基于getAndAddInt实现
  */
 @Test
 public void getAndAddInt() throws InterruptedException {

User u = new User(17, "zhangsan");
   CountDownLatch downLatch = new CountDownLatch(10);
   System.out.println("u.getAge() = " + u.getAge());
   for (int i = 0; i < 10; i++) {

new Thread(new Runnable() {
       @Override
       public void run() {
         downLatch.countDown();
         int val = unSafe.getAndAddInt(u, 12L, 1);
         System.out.println(Thread.currentThread().getName() + " val = " + val);
       }
     }).start();

}
   Thread.sleep(5000);
   System.out.println("u.getAge() = " + u.getAge());
 }

@Test
 public void getAndSetInt() throws InterruptedException {
   User u = new User(17, "zhangsan");
   CountDownLatch downLatch = new CountDownLatch(10);
   System.out.println("u.getAge() = " + u.getAge());
   for (int i = 0; i < 10; i++) {

new Thread(new Runnable() {
       @Override
       public void run() {
         downLatch.countDown();
         int val = unSafe.getAndSetInt(u, 12L, 10);
         System.out.println(Thread.currentThread().getName() + " val = " + val);
       }
     }).start();

}
   Thread.sleep(5000);
   System.out.println("u.getAge() = " + u.getAge());
 }

@Test
 public void getIntVolatile() {

for (int i = 0; i < 10; i++) {
     u.setAge(i);
     /**
      * @param obj  the object containing the field to modify.
      * @param offset the offset of the integer field within <code>obj</code>.
      * @return
      */
     int age = unSafe.getIntVolatile(u, 12L);
     System.out.println("age = " + age);
   }
 }

// 系统负载采样的接口
 @Test
 public void getLoadAverage() {
   double[] nums = new double[8];
   int val = unSafe.getLoadAverage(nums, 8);
   System.out.println(val);
 }

/**
  * //内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
  * public native void loadFence();
  * <p>
  * <p>
  * 参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
  */

@Test
 public void loadFence() {
   //java.util.concurrent.locks.StampedLock.validate
   unSafe.loadFence();
 }

/**
  * //内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
  * public native void storeFence();
  * 参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
  */
 @Test
 public void storeFence() {
 }

/**
  * //内存屏障,禁止load、store操作重排序
  * public native void fullFence();
  * 参见:https://tech.meituan.com/2019/02/14/talk-about-java-magic-class-unsafe.html
  */
 @Test
 public void fullFence() {
 }

@Test
 public void shouldBeInitialized() {
   boolean shouldBeInitialized = unSafe.shouldBeInitialized(String.class);
   System.out.println(shouldBeInitialized);
   shouldBeInitialized = unSafe.shouldBeInitialized(User.class);
   System.out.println(shouldBeInitialized);
 }

/**
  * synchronized 的一种实现获取锁
  *
  * @throws InterruptedException
  */
 @Test
 public void monitorEnter() throws InterruptedException {

unSafe.monitorEnter(u);
   new Thread(new Runnable() {
     @Override
     public void run() {
       synchronized (u) {
         System.out.println("==u lock got ==");
       }
     }
   }).start();

Thread.sleep(2000);
   unSafe.monitorExit(u);
 }

@Test
 public void compareAndSwap() {
//    unSafe.compareAndSwapInt(对象, 对象中的字段偏移, 期望值, 设置值)
//    unSafe.compareAndSwapLong(对象, 对象中的字段偏移, 期望值, 设置值)
//    unSafe.compareAndSwapObject(对象, 对象中的字段偏移, 期望值, 设置值)
 }

@Test
 public void t() {
   // 方法签名
   // public void copyMemory(Object srcBase, long srcOffset,Object destBase, long destOffset, long bytes)
   // unSafe.copyMemory();
 }

}

class ClassIsLoad {

static {
   System.out.println("ClassIsLoad class Is Load !");
 }
}

来源:https://www.cnblogs.com/leodaxin/p/13556615.html

标签:Java,Unsafe,类
0
投稿

猜你喜欢

  • SpringCloud hystrix断路器与局部降级全面介绍

    2023-10-28 17:29:11
  • Spring boot整合mybatis实现过程图解

    2023-01-15 09:31:05
  • .net中常用的正则表达式

    2021-06-12 00:15:44
  • 详解Android App中使用VideoView来实现视频播放的方法

    2022-05-01 00:20:11
  • WinForm项目开发中NPOI用法实例解析

    2021-09-17 16:53:57
  • Android App中实现向右滑动销毁功能的要点解析

    2022-09-14 11:25:17
  • C# 读写自定义的Config文件的实现方法

    2022-09-08 23:22:35
  • IDEA使用GsonFormat完成JSON和JavaBean之间的转换

    2021-06-10 11:30:30
  • C# readnodefile()不能读取带有文件名为汉字的osg文件解决方法

    2021-11-08 07:41:00
  • Java for循环标签跳转到指定位置的示例详解

    2022-10-04 17:52:31
  • Kotlin Option与Either及Result实现异常处理详解

    2022-03-31 17:33:18
  • RocketMQ特性Broker存储事务消息实现

    2022-07-10 20:42:13
  • java实现微信点餐申请微信退款

    2022-10-29 19:07:57
  • c# 实现模糊PID控制算法

    2022-12-06 13:06:22
  • android BitmapFactory.Options使用方法详解

    2023-05-04 08:50:20
  • java 查找替换pdf中的指定文本

    2022-08-17 14:04:46
  • C#获取进程和对进程的操作

    2021-07-14 19:50:35
  • IDEA怎么生成UML类图的实现

    2023-12-07 15:40:21
  • Java字符串驼峰与下换线格式转换如何实现

    2022-02-18 09:56:14
  • 浅谈java中对集合对象list的几种循环访问

    2022-02-11 02:32:33
  • asp之家 软件编程 m.aspxhome.com