文档章节

Understanding sun.misc.Unsafe

无断无灭无住无念
 无断无灭无住无念
发布于 2017/05/19 13:28
字数 1020
阅读 23
收藏 0
点赞 0
评论 0

一:定义

private static final Unsafe theUnsafe;
static {
		registerNatives();
		Reflection.registerMethodsToFilter(Unsafe.class,
				new String[] { "getUnsafe" });
		theUnsafe = new Unsafe();
		ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);
		ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
		ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class);
		ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class);
		ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);
		ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class);
		ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class);
		ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class);
		ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class);
		ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
		ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class);
		ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class);
		ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class);
		ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);
		ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class);
		ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class);
		ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class);
		ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class);
		ADDRESS_SIZE = theUnsafe.addressSize();
	}

theUnsafe属性是私有属性,且构造器私有,只提供了getUnsafe()方法拿到实例。

@CallerSensitive
	public static Unsafe getUnsafe() {
		Class arg = Reflection.getCallerClass();
		if (!VM.isSystemDomainLoader(arg.getClassLoader())) {
			throw new SecurityException("Unsafe");
		} else {
			return theUnsafe;
		}
	}

Reflection.getCallerClass()返回调用栈中最外层调用类。 在 HotSpot虚拟机中中,bootstrap启动类加载器引用是null。在应用类中,不会用到这个ClassLoader。所以,isSystemDomainLoader()的参数arg.getClassLoader()如果不是null,说明调用的ClassLoader不是Bootstrap启动类加载器,对Unsafe来说是不安全的,直接返回SecurityException. 所以不能通过getUnsafe()得到Unsafe实例。

二:获得实例

有两种方法可以获得theUnsafe属性。

1.反射属性

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

2.反射构造器

Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
Unsafe unsafe = unsafeConstructor.newInstance();

三:用法

3.1 不通过构造器直接实例化指定Class的实例

class ClassWithExpensiveConstructor {

		  private final int value;

		  private ClassWithExpensiveConstructor() {
		    value = doExpensiveLookup();
		  }

		  private int doExpensiveLookup() {
		    try {
		      Thread.sleep(2000);
		    } catch (InterruptedException e) {
		      e.printStackTrace();
		    }
		    return 1;
		  }

		  public int getValue() {
		    return value;
		  }
		}


ClassWithExpensiveConstructor obj=ClassWithExpensiveConstructor.class.cast(unsafe.allocateInstance(ClassWithExpensiveConstructor.class));
Assert.assertEquals(obj.getValue(),0);

3.2 本地内存分配

getXXX(Object target, long offset) - 读取对象target位置上 指定偏移量offset的值

putXXX(Object target, long offset, XXX value) - 设置对象target位置上 指定偏移量offset的值

compareAndSwapXXX(Object target, long offset, long expectedValue, long value) -读取对象target位置上 指定偏移量offset的值 如果该值为expectedValue,则设置为新值value。该操作是原子操作。


public final native boolean compareAndSwapObject(Object arg0, long arg1,
			Object arg3, Object arg4);

public final native boolean compareAndSwapInt(Object arg0, long arg1,
			int arg3, int arg4);

public final native boolean compareAndSwapLong(Object arg0, long arg1,
			long arg3, long arg5);

@Test
public void testCopy() throws Exception {
  long address = unsafe.allocateMemory(4L);
  unsafe.putInt(address, 100);
  long otherAddress = unsafe.allocateMemory(4L);
  unsafe.copyMemory(address, otherAddress, 4L);
  assertEquals(100, unsafe.getInt(otherAddress));
}
public void place(Object o, long address) throws Exception {
  Class clazz = o.getClass();
  do {
    for (Field f : clazz.getDeclaredFields()) {
      if (!Modifier.isStatic(f.getModifiers())) {
        long offset = unsafe.objectFieldOffset(f);
        if (f.getType() == long.class) {
          unsafe.putLong(address + offset, unsafe.getLong(o, offset));
        } else if (f.getType() == int.class) {
          unsafe.putInt(address + offset, unsafe.getInt(o, offset));
        } else {
          throw new UnsupportedOperationException();
        }
      }
    }
  } while ((clazz = clazz.getSuperclass()) != null);
}

public Object read(Class clazz, long address) throws Exception {
  Object instance = unsafe.allocateInstance(clazz);
  do {
    for (Field f : clazz.getDeclaredFields()) {
      if (!Modifier.isStatic(f.getModifiers())) {
        long offset = unsafe.objectFieldOffset(f);
        if (f.getType() == long.class) {
          unsafe.putLong(instance, offset, unsafe.getLong(address + offset));
        } else if (f.getType() == int.class) {
          unsafe.putLong(instance, offset, unsafe.getInt(address + offset));
        } else {
          throw new UnsupportedOperationException();
        }
      }
    }
  } while ((clazz = clazz.getSuperclass()) != null);
  return instance;
}

@Test
public void testObjectAllocation() throws Exception {
  long containerSize = sizeOf(Container.class);
  long address = unsafe.allocateMemory(containerSize);
  Container c1 = new Container(10, 1000L);
  Container c2 = new Container(5, -10L);
  place(c1, address);
  place(c2, address + containerSize);
  Container newC1 = (Container) read(Container.class, address);
  Container newC2 = (Container) read(Container.class, address + containerSize);
  assertEquals(c1, newC1);
  assertEquals(c2, newC2);
}

此外,并发编程中底层普遍使用AbstractQueuedSynchronizer类维护锁状态变量state。

   /**
     * The synchronization state.
     */
    private volatile int state;

对于state的更新,使用Unsafe直接修改内存中的值。

/**
    * Setup to support compareAndSet. We need to natively implement
    * this here: For the sake of permitting future enhancements, we
    * cannot explicitly subclass AtomicInteger, which would be
    * efficient and useful otherwise. So, as the lesser of evils, we
    * natively implement using hotspot intrinsics API. And while we
    * are at it, we do the same for other CASable fields (which could
    * otherwise be done with atomic field updaters).
    */
   private static final Unsafe unsafe = Unsafe.getUnsafe();
   private static final long stateOffset;
   private static final long headOffset;
   private static final long tailOffset;
   private static final long waitStatusOffset;
   private static final long nextOffset;

   static {
       try {
           //获得state属性在内存中offset
           stateOffset = unsafe.objectFieldOffset
               (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
           headOffset = unsafe.objectFieldOffset
               (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
           tailOffset = unsafe.objectFieldOffset
               (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
           waitStatusOffset = unsafe.objectFieldOffset
               (Node.class.getDeclaredField("waitStatus"));
           nextOffset = unsafe.objectFieldOffset
               (Node.class.getDeclaredField("next"));

       } catch (Exception ex) { throw new Error(ex); }
   }


/**
    * Atomically sets synchronization state to the given updated
    * value if the current state value equals the expected value.
    * This operation has memory semantics of a {@code volatile} read
    * and write.
    *
    * @param expect the expected value
    * @param update the new value
    * @return {@code true} if successful. False return indicates that the actual
    *         value was not equal to the expected value.
    */
   protected final boolean compareAndSetState(int expect, int update) {
       // See below for intrinsics setup to support this
       //比较并原子更新state
       return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
   }

3.3 throw Exception

public native void throwException(Throwable arg0);

@Test(expected =ResourceNotFoundException.class)
	public void test4() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
		Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
		unsafeConstructor.setAccessible(true);
		Unsafe unsafe = unsafeConstructor.newInstance();
		unsafe.throwException(new ResourceNotFoundException());
	}

3.4 native concurrency

@Test
public void testPark() throws Exception {
  final boolean[] run = new boolean[1];
  Thread thread = new Thread() {
    @Override
    public void run() {
      unsafe.park(true, 100000L);
      run[0] = true;
    }
  };
  thread.start();
  unsafe.unpark(thread);
  thread.join(100L);
  assertTrue(run[0]);
}

四:参考资料

http://www.javaworld.com/article/2952869/java-platform/understanding-sun-misc-unsafe.html

http://mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

https://dzone.com/articles/understanding-sunmiscunsafe

© 著作权归作者所有

共有 人打赏支持
无断无灭无住无念
粉丝 13
博文 39
码字总数 29962
作品 0
苏州
CTO(技术副总裁)
Java 9 发布在即,Oracle OpenJDK 着手优化 Unsafe 类

java 9正式版预计在2017年2季度发布,目前大部分JEP已经基本成型。其中,最关键特性或许是JEP 261, 该JEP实现了java平台的模块系统, 具体说明可以参见JSR376。 模块系统依赖于JEP260(封装了...

oschina ⋅ 2016/05/07 ⋅ 46

线程进阶:多任务处理(17)——Java中的锁(Unsafe基础)

1. 概述 本专题在之前的文章中详细介绍了Java中最常使用的一种锁机制——同步锁。但是同步锁肯定是不适合在所有应用场景中使用的。所以从本文开始,笔者将试图通过两到三篇文章的篇幅向读者介...

yinwenjie ⋅ 2017/06/14 ⋅ 0

androidstudio 引用的jdk不包含sun.misc包?

我想使用sun.misc.Unsafe这个类,但是发现androidstudio引用的jdk里却没有sun.misc这个包是怎么回事?androidstudio的截图如下: 代码中提示了红色: 但是在eclipse中却有这个包: 我想在AS引...

bobJiao ⋅ 2016/12/29 ⋅ 0

ConcurrentLinkedQueue cas实现分析

淘宝清英在此文对ConcurrentLinkedQueue做了详细分析 http://www.infoq.com/cn/articles/ConcurrentLinkedQueue cas实现没有涉及,我又读了一下ConcurrentLinkedQueue.Node类,记录此文作为补...

yingtju ⋅ 2013/08/28 ⋅ 0

java.util.concurrent.atomic 学习(二)

Aomic包下有四种数据类型: AomicBoolean, AomicInteger, AomicLong和AomicReferrence(针对Object的)以及它们的数组类型, 和相对应的AtomicXXXFieldUpdater. 各种数据类型中所有的原子操作都...

软通快递 ⋅ 2017/06/01 ⋅ 0

GSON混淆后执行错误

You need to add these lines to your proguard so that gson class is kept while signing your app. ##---------------Begin: proguard configuration for Gson ---------- # Gson uses ge......

legend3 ⋅ 2015/05/23 ⋅ 0

sun内部使用的那些东西

sun.misc.Unsafe这个类与并发相关的,提供了硬件上面的原子操作,以及一些其他的功能如:绕过所有的东西,实例化任意类的对象等等,反正Unsafe这个类很重要。 sun.reflect.ReflectionFactor...

只想一个人静一静 ⋅ 2014/02/24 ⋅ 0

HashMap,HashTable,ConcurrentHashMap的区别

之前都是JDK1.5,1.6版本的,今天不巧系统升级了。。JavaSE6跟系统不兼容。。只好升级了JDK1.8 然后对比一下,发现各个版本的CurrentHashMap竟然还都不一样。。 这里就对比一下1.8的了。。 ...

Eviltuzki ⋅ 2015/11/02 ⋅ 4

sun.misc.Unsafe的各种神技Unsafe

sun.misc.Unsafe包 Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率。但是,它是一把双刃剑:正如它的名字所预示的那样,它是Unsafe的...

HJCui ⋅ 05/24 ⋅ 0

Java直接内存访问的技巧

Java直接内存访问的技巧 Feb262013 作者:逍遥冲 发布:2013-02-26 21:35 分类:JavaSE 阅读:23,814 浏览数 1条评论 Java被设计成一个安全,可管理的环境,然而 Java HotSpot有一个后门,提...

tantexian ⋅ 2016/07/26 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

熊掌号收录比例对于网站原创数据排名的影响[图]

从去年下半年开始,我在写博客了,因为我觉得业余写写博客也还是很不错的,但是从2017年下半年开始,百度已经推出了原创保护功能和熊掌号平台,为此,我也提交了不少以前的老数据,而这些历史...

原创小博客 ⋅ 40分钟前 ⋅ 0

LVM讲解、磁盘故障小案例

LVM LVM就是动态卷管理,可以将多个硬盘和硬盘分区做成一个逻辑卷,并把这个逻辑卷作为一个整体来统一管理,动态对分区进行扩缩空间大小,安全快捷方便管理。 1.新建分区,更改类型为8e 即L...

蛋黄Yolks ⋅ 59分钟前 ⋅ 0

Hadoop Yarn调度器的选择和使用

一、引言 Yarn在Hadoop的生态系统中担任了资源管理和任务调度的角色。在讨论其构造器之前先简单了解一下Yarn的架构。 上图是Yarn的基本架构,其中ResourceManager是整个架构的核心组件,它负...

p柯西 ⋅ 今天 ⋅ 0

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 今天 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部