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


Posted in Python onSeptember 15, 2020

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 !");
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python tkinter实现的图片移动碰撞动画效果【附源码下载】
Jan 04 Python
Python实现的爬虫刷回复功能示例
Jun 07 Python
python实现人人自动回复、抢沙发功能
Jun 08 Python
django rest framework 数据的查找、过滤、排序的示例
Jun 25 Python
使用Python实现租车计费系统的两种方法
Sep 29 Python
python3.7 使用pymssql往sqlserver插入数据的方法
Jul 08 Python
Python with语句和过程抽取思想
Dec 23 Python
python代码中怎么换行
Jun 17 Python
Python自动巡检H3C交换机实现过程解析
Aug 14 Python
pytorch交叉熵损失函数的weight参数的使用
May 24 Python
手把手教你使用TensorFlow2实现RNN
Jul 15 Python
python自动化测试通过日志3分钟定位bug
Nov 20 Python
python装饰器实现对异常代码出现进行自动监控的实现方法
Sep 15 #Python
Python requests上传文件实现步骤
Sep 15 #Python
python -v 报错问题的解决方法
Sep 15 #Python
基于Python正确读取资源文件
Sep 14 #Python
Django框架安装及项目创建过程解析
Sep 14 #Python
通过代码实例了解Python sys模块
Sep 14 #Python
基于python实现简单C/S模式代码实例
Sep 14 #Python
You might like
PHP得到某段时间区间的时间戳 php定时任务
2012/04/12 PHP
编写PHP脚本来实现WordPress中评论分页的功能
2015/12/10 PHP
laravel admin实现分类树/模型树的示例代码
2020/06/10 PHP
js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
2012/05/16 Javascript
网站如何做到完全不需要jQuery也可以满足简单需求
2013/06/27 Javascript
jquery submit ie6下失效的原因分析及解决方法
2013/11/15 Javascript
Jquery实现控件的隐藏和显示实例
2014/02/08 Javascript
JavaScript数组深拷贝和浅拷贝的两种方法
2014/04/16 Javascript
jquery 操作css样式、位置、尺寸方法汇总
2014/11/28 Javascript
javascript事件模型实例分析
2015/01/30 Javascript
js简单工厂模式用法实例
2015/06/30 Javascript
jquery图片放大镜效果
2017/06/23 jQuery
ionic2屏幕适配实现适配手机、平板等设备的示例代码
2017/08/11 Javascript
vue 根据数组中某一项的值进行排序的方法
2018/08/30 Javascript
vue表单中遍历表单操作按钮的显示隐藏示例
2019/10/30 Javascript
你不知道的SpringBoot与Vue部署解决方案
2020/11/09 Javascript
Python 文件操作的详解及实例
2017/09/18 Python
Python解析并读取PDF文件内容的方法
2018/05/08 Python
python字典一键多值实例代码分享
2019/06/14 Python
Django时区详解
2019/07/24 Python
python cumsum函数的具体使用
2019/07/29 Python
Python实现使用dir获取类的方法列表
2019/12/24 Python
基于Python数据分析之pandas统计分析
2020/03/03 Python
Python的in,is和id函数代码实例
2020/04/18 Python
德国健康生活方式网上商店:Landkaufhaus Mayer
2019/03/12 全球购物
澳大利亚墨水站Ink Station:墨水和碳粉打印机墨盒
2019/03/24 全球购物
英国最受欢迎的母婴精品品牌:JoJo Maman BéBé
2021/02/17 全球购物
总裁办公室主任职责
2014/01/02 职场文书
计算机相关的自我评价
2014/01/15 职场文书
20年同学聚会邀请函
2014/02/04 职场文书
秘书英文求职信
2014/04/16 职场文书
生物工程专业求职信
2014/09/03 职场文书
pdf论文中python画的图Type 3 fonts字体不兼容的解决方案
2021/04/24 Python
python基于tkinter实现gif录屏功能
2021/05/19 Python
SQL Server2019数据库备份与还原脚本,数据库可批量备份
2021/11/20 SQL Server
InterProcessMutex实现zookeeper分布式锁原理
2022/03/21 Java/Android