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中使用hashlib模块处理算法的教程
Apr 28 Python
详解Django rest_framework实现RESTful API
May 24 Python
Windows下PyCharm安装图文教程
Aug 27 Python
Python判断一个文件夹内哪些文件是图片的实例
Dec 07 Python
Python实现元素等待代码实例
Nov 11 Python
使用TFRecord存取多个数据案例
Feb 17 Python
Django实现从数据库中获取到的数据转换为dict
Mar 27 Python
增大python字体的方法步骤
Jul 05 Python
Python3基于plotly模块保存图片表格
Aug 03 Python
python中altair可视化库实例用法
Jan 26 Python
Python Django 后台管理之后台模型属性详解
Apr 25 Python
Python基于Opencv识别两张相似图片
Apr 25 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记录代码执行时间(实现代码)
2013/07/05 PHP
PHP中引用类型和值类型功能与用法示例
2019/02/26 PHP
javascript 浏览器检测代码精简版
2010/03/04 Javascript
理解Javascript_14_函数形式参数与arguments
2010/10/20 Javascript
移动节点的jquery代码
2014/01/13 Javascript
一个小例子解释如何来阻止Jquery事件冒泡
2014/07/17 Javascript
jQuery根据ID获取input、checkbox、radio、select的示例
2014/08/11 Javascript
node+express+jade制作简单网站指南
2014/11/26 Javascript
浅谈javascript事件取消和阻止冒泡
2015/05/26 Javascript
移动端js图片查看器
2016/11/17 Javascript
js通过指定下标或指定元素进行删除数组的实例
2017/01/12 Javascript
原生js实现瀑布流布局
2017/03/08 Javascript
AngularJS中使用three.js的实例详解
2017/07/21 Javascript
Vue用v-for给循环标签自身属性添加属性值的方法
2018/10/18 Javascript
在 Vue.js中优雅地使用全局事件的方法
2019/02/01 Javascript
Python random模块(获取随机数)常用方法和使用例子
2014/05/13 Python
Python实现简单的四则运算计算器
2016/11/02 Python
Python编程判断一个正整数是否为素数的方法
2017/04/14 Python
python实战串口助手_解决8串口多个发送的问题
2019/06/12 Python
Python分析彩票记录并预测中奖号码过程详解
2019/07/09 Python
python+numpy按行求一个二维数组的最大值方法
2019/07/09 Python
python flask几分钟实现web服务的例子
2019/07/26 Python
pytorch 实现删除tensor中的指定行列
2020/01/13 Python
pytorch方法测试——激活函数(ReLU)详解
2020/01/15 Python
python实现百度OCR图片识别过程解析
2020/01/17 Python
pycharm安装及如何导入numpy
2020/04/03 Python
python调用有道智云API实现文件批量翻译
2020/10/10 Python
荷兰街头时尚之家:Funkie House
2019/03/18 全球购物
解决方案设计综合面试题
2015/08/31 面试题
公务员职业生涯规划书范文  
2014/01/19 职场文书
护理人员的自我评价分享
2014/03/15 职场文书
教师竞聘演讲稿
2014/05/16 职场文书
小学德育工作总结2015
2015/05/12 职场文书
入党申请书怎么写?
2019/06/21 职场文书
教你用Python写一个植物大战僵尸小游戏
2021/04/25 Python
Python max函数中key的用法及原理解析
2021/06/26 Python