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中二维阵列的变换实例
Oct 09 Python
Python中的defaultdict模块和namedtuple模块的简单入门指南
Apr 01 Python
在Django的视图中使用数据库查询的方法
Jul 16 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
Aug 30 Python
python with提前退出遇到的坑与解决方案
Jan 05 Python
django表单实现下拉框的示例讲解
May 29 Python
python自带tkinter库实现棋盘覆盖图形界面
Jul 17 Python
详解Python3 pickle模块用法
Sep 16 Python
python自动化测试无法启动谷歌浏览器问题
Oct 10 Python
Tensorflow加载Vgg预训练模型操作
May 26 Python
浅谈Python中的字符串
Jun 10 Python
Keras中的两种模型:Sequential和Model用法
Jun 27 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中在PDO中使用事务(Transaction)
2011/05/14 PHP
PHP函数http_build_query使用详解
2014/08/20 PHP
ThinkPHP中公共函数路径和配置项路径的映射分析
2014/11/22 PHP
php实现的微信红包算法分析(非官方)
2015/09/25 PHP
PHP实现双链表删除与插入节点的方法示例
2017/11/11 PHP
Js 弹出框口并返回值的两种常用方法
2010/12/30 Javascript
滚动图片效果 jquery实现回旋滚动效果
2013/01/08 Javascript
javascript数组的使用
2013/03/28 Javascript
jquery插件开发注意事项小结
2013/06/04 Javascript
弹出最简单的模式化遮罩层的js代码
2013/12/04 Javascript
JsRender for index循环索引用法详解
2014/10/31 Javascript
jQuery选择器源码解读(三):tokenize方法
2015/03/31 Javascript
javascript解析xml实现省市县三级联动的方法
2015/07/25 Javascript
使用 stylelint检查CSS_StyleLint
2016/04/28 Javascript
Bootstrap中的fileinput 多图片上传及编辑功能
2016/09/05 Javascript
AngularJS使用ng-repeat和ng-if实现数据的删选显示效果示例【适用于表单数据的显示】
2016/12/13 Javascript
js下载文件并修改文件名
2017/05/08 Javascript
Axios学习笔记之使用方法教程
2017/07/21 Javascript
详解webpack进阶之loader篇
2017/08/23 Javascript
学习Vue组件实例
2018/04/28 Javascript
Vue混入mixins滚动触底的方法
2019/11/22 Javascript
[01:00:06]加油DOTA_EP01_网络版
2014/08/09 DOTA
[48:52]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第一局
2016/02/25 DOTA
python实现银联支付和支付宝支付接入
2019/05/07 Python
python实现删除列表中某个元素的3种方法
2020/01/15 Python
详解css3 Transition属性(平滑过渡菜单栏案例)
2017/09/05 HTML / CSS
利用Canvas模仿百度贴吧客户端loading小球的方法示例
2017/08/13 HTML / CSS
澳大利亚领先的在线美容商城:Adore Beauty
2017/04/14 全球购物
Pam & Gela官网:美国性感前卫女装品牌
2018/07/19 全球购物
UNIONBAY官网:美国青少年服装品牌
2019/03/26 全球购物
毕业自我鉴定总结
2014/03/24 职场文书
个人股份合作协议书
2014/10/24 职场文书
2014年城管工作总结
2014/11/20 职场文书
2014年宣传思想工作总结
2014/12/10 职场文书
网吧温馨提示
2015/07/17 职场文书
MySQL对数据表已有表进行分区表的实现
2021/11/01 MySQL