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

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

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

(0)

相关推荐

  • 简单谈一谈Java中的Unsafe类

    Unsafe类是啥? Java最初被设计为一种安全的受控环境.尽管如此,Java HotSpot还是包含了一个"后门",提供了一些可以直接操控内存和线程的低层次操作.这个后门类--sun.misc.Unsafe--被JDK广泛用于自己的包中,如java.nio和java.util.concurrent.但是丝毫不建议在生产环境中使用这个后门.因为这个API十分不安全.不轻便.而且不稳定.这个不安全的类提供了一个观察HotSpot JVM内部结构并且可以对其进行修改.有时它可以被用来在不

  • Java并发编程学习之Unsafe类与LockSupport类源码详析

    一.Unsafe类的源码分析 JDK的rt.jar包中的Unsafe类提供了硬件级别的原子操作,Unsafe里面的方法都是native方法,通过使用JNI的方式来访问本地C++实现库. rt.jar 中 Unsafe 类主要函数讲解, Unsafe 类提供了硬件级别的原子操作,可以安全的直接操作内存变量,其在 JUC 源码中被广泛的使用,了解其原理为研究 JUC 源码奠定了基础. 首先我们先了解Unsafe类中主要方法的使用,如下: 1.long objectFieldOffset(Field

  • Java中的魔法类:sun.misc.Unsafe示例详解

    前言 Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率.但是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的,它所分配的内存需要手动free(不被GC回收).Unsafe类,提供了JNI某些功能的简单替代:确保高效性的同时,使事情变得更简单. 这个类是属于sun.* API中的类,并且它不是J2SE中真正的一部份,因此你可能找不到任何的官方文档,更可悲的是,它也没有比较好的代码文档. 这篇文章主要是以下文章的整理.翻译.

  • 一篇看懂Java中的Unsafe类

    前言 本文主要给大家介绍了关于Java中Unsafe类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoop.Kafka等. 使用Unsafe可用来直接访问系统内存资源并进行自主管理,Unsafe类在提升Java运行效率,增强Java语言底层操作能力方面起了很大的作用. Un

  • Java中unsafe操作实例总结

    Unsafe是Java无锁操作的基石,在无锁并发类中都少不了它们的身影,比如ConcurrentHashMap, ConcurrentLinkedQueue, 都是由Unsafe类来实现的.相对于与Java中的锁,它基本无开销,会原地等待.本文主要介绍下Unsafe中的主要操作. 1 compareAndSwap /** * 比较obj的offset处内存位置中的值和期望的值,如果相同则更新.此更新是不可中断的. * * @param obj 需要更新的对象 * @param offset ob

  • java Unsafe详细解析

    问题 (1)Unsafe是什么? (2)Unsafe只有CAS的功能吗? (3)Unsafe为什么是不安全的? (4)怎么使用Unsafe? 简介 Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用. 但是,为了更好地了解java的生态体系,我们应该去学习它,去了解它,不求深入到底层的C/C++代码,但求能了解它的基本功能. 获取Unsafe的实例 查看Unsafe的源码我们会发现它提供了一个getUnsafe()的静态方法. @CallerSens

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

    Unsafe类介绍 第一次看到这个类时被它的名字吓到了,居然还有一个类自名Unsafe?读完本文,大家也能发现Unsafe类确实有点不那么安全,它能实现一些不那么常见的功能. Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题.过度的使用Unsafe类会使得出错的几率变大,因此Java官方并不建议使用的,官方文档也几乎没有.Oracle正在计划从Java 9中去掉Unsafe类,如果真是如此影响就太大了. Unsafe类提供了以下这些功能: 一.内存管理.

  • Java Unsafe 类的讲解

    目录 一.Unsafe类是啥? 二.为什么叫Unsafe? 三.如何使用Unsafe? 1. 获取Unsafe实例 2. 通过Unsafe分配使用堆外内存 3. 操作类对象 4. 线程挂起和恢复 5. CAS操作 6. Clone 一.Unsafe类是啥? Java最初被设计为一种安全的受控环境.尽管如此,Java HotSpot还是包含了一个"后门",提供了一些可以直接操控内存和线程的低层次操作.这个后门类--sun.misc.Unsafe--被JDK广泛用于自己的包中,如java.

  • java LinkedList类详解及实例代码

    java  LinkedList类详解 LinkedList的特有功能 A:添加功能 public void addFirst(Object e); public void addLast(Object e); B:特有功能 public Object getFirst(); public Object getLast(); C:删除功能 public Object removeFirst(); public Object removeLast(); 实例代码: import java.util

  • java  LinkedList类详解及实例代码

    java  LinkedList类详解 LinkedList的特有功能 A:添加功能 public void addFirst(Object e); public void addLast(Object e); B:特有功能 public Object getFirst(); public Object getLast(); C:删除功能 public Object removeFirst(); public Object removeLast(); 实例代码: import java.util

  • Java Vector类详解及实例代码

    Java Vector类  Vector的特有功能 Vector出现较早,比集合更早出现 1:添加功能 public void addElement(Object obj);//用add()替代 2:获取功能 public Object elementAt(int index);//用get()替代 public Enumeration elements();//返回的是实现类的对象,用Iterator iterator() import java.util.Enumeration; impor

  • Java继承的问题引导和测试代码

    目录 1.1.定义 1.2.创建子类 1.3.继承的基本规则 1. 子类可以继承除父类构造函数以外的一切成员 2. 虽然子类继承了父类的私有成员,但子类并不能直接访问,如果想要访问私有成员必须借助父类的公共接口. 3. 只支持单一继承,不支持多重继承,但支持多层继承 1.4.阻止继承 1.4.1.final修饰符 1. final 修饰的类不能被继承 2. final 修饰的方法不能被覆盖 3. final 修饰的变量(成员变量和局部变量)必须显式初始化,且只能初始化一次. 4. 常量定义的标准

  • java Arrays类详解及实例代码

    最近做项目 用到Arrays 类,这里整理下,希望大家能够掌握Arrays . 1.Arrays类概述 针对数组进行操作的工具类. 提供了排序,查找等功能. 2.成员方法 public static String toString(int[] a) public static void sort(int[] a) public static int binarySearch(int[] a,int value) package com; import java.util.Arrays; /**

  • Java MD5消息摘要算法原理及实现代码

    md5 属于hash算法一类,是不可逆的消息摘要算法.与对称加密和非对称加密算法不一样,不需要加密密钥. 注意: md5不是加密算法,只是将数据进行散列计算后生成一个唯一值的算法,没有加密密钥也没有解密密钥. 下面说的md5加密是指对密码加密成32位长度字符串的过程 md5可以用于密码的加密,如123456,加密后的字符串,在很大条件下不能被电脑强行破解出来,只能通过字典匹配的方式同样用md5加密后的字符串进行比较破解. MessageDigest消息摘要是安全的单向散列函数,它将任意大小的字符

  • Java内部类的全限定名规律代码示例

    简述: 由于最近遇到不少需要反射的类,而其中不乏内部类,在此总结一下内部类的全限定名的规律. 成员内部类 测试结果表明,无论成员内部类是否为静态,其全限定名都使用如下命名方法: 包名.外部类名$内部类名 测试代码: package com.test; public class InnerClassTest { static class StaticInner{ } class Inner{ } public static void main(String[] args) { StaticInne

  • Java设计模式之代理模式原理及实现代码分享

    简介 Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术.生活中的方方面面都可以虚拟到代码中.代理模式所讲的就是现实生活中的这么一个概念:中介. 代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用. 代理模式包含如下角色: ISubject:抽象主题角色,是一个接口.该接口是对象和它的代理共用的接口. RealSubject:真实主题角色,是实现抽象主题接口的类. Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实

随机推荐