java中Unsafe使用讲解

前段时间因为看JUC的源码,里面有大量关于unsafe的操作,所以就来看看了.写点笔记总结下(本文基于jdk1.8):

unsafe可以帮我们直接去操作硬件资源,当然了是借助java的jit来进行的,官方不推荐使用,因为不安全,例如你使用unsafe创建一个超级大的数组,但是这个数组jvm是不管理的,只能你自己操作,容易oom,也不利于资源的回收.

好了,下面我们来看代码,

1.获取unsafe

 
  1. //1.最简单的使用方式是基于反射获取Unsafe实例

  2. Field f = Unsafe.class.getDeclaredField("theUnsafe");

  3. f.setAccessible(true);

  4. Unsafe unsafe = (Unsafe) f.get(null);

2.获取unsafe

 
  1. private static Unsafe unsafe = null;

  2. private static Field getUnsafe = null;

  3.  
  4. static {

  5. try {

  6. getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");

  7. getUnsafe.setAccessible(true);

  8. unsafe = (Unsafe) getUnsafe.get(null);

  9. } catch (NoSuchFieldException e) {

  10. e.printStackTrace();

  11. } catch (IllegalAccessException e) {

  12. e.printStackTrace();

  13. }

  14. }

随便只要你高兴,都可以获取到unfase,因为涉及到unfase 的权限问题,所以,我们只能使用这种方式获取,不然就是权限异常,

操作方法:

 
  1. /**

  2. * 操作数组:

  3. * 可以获取数组的在内容中的基本偏移量(arrayBaseOffset),获取数组内元素的间隔(比例),

  4. * 根据数组对象和偏移量获取元素值(getObject),设置数组元素值(putObject),示例如下。

  5. */

  6. String[] strings = new String[]{"1", "2", "3"};

  7. long i = unsafe.arrayBaseOffset(String[].class);

  8. System.out.println("string[] base offset is :" + i);

  9.  
  10. //every index scale

  11. long scale = unsafe.arrayIndexScale(String[].class);

  12. System.out.println("string[] index scale is " + scale);

  13.  
  14. //print first string in strings[]

  15. System.out.println("first element is :" + unsafe.getObject(strings, i));

  16.  
  17. //set 100 to first string

  18. unsafe.putObject(strings, i + scale * 0, "100");

  19.  
  20. //print first string in strings[] again

  21. System.out.println("after set ,first element is :" + unsafe.getObject(strings, i + scale * 0));

 

 
  1. /**

  2. * 对象操作

  3. * 实例化Data

  4. *

  5. * 可以通过类的class对象创建类对象(allocateInstance),获取对象属性的偏移量(objectFieldOffset)

  6. * ,通过偏移量设置对象的值(putObject)

  7. *

  8. * 对象的反序列化

  9. * 当使用框架反序列化或者构建对象时,会假设从已存在的对象中重建,你期望使用反射来调用类的设置函数,

  10. * 或者更准确一点是能直接设置内部字段甚至是final字段的函数。问题是你想创建一个对象的实例,

  11. * 但你实际上又不需要构造函数,因为它可能会使问题更加困难而且会有副作用。

  12. *

  13. */

  14. //调用allocateInstance函数避免了在我们不需要构造函数的时候却调用它

  15. Data data = (Data) unsafe.allocateInstance(Data.class);

  16. data.setId(1L);

  17. data.setName("unsafe");

  18. System.out.println(data);

  19.  
  20. //返回成员属性在内存中的地址相对于对象内存地址的偏移量

  21. Field nameField = Data.class.getDeclaredField("name");

  22. long fieldOffset = unsafe.objectFieldOffset(nameField);

  23. //putLong,putInt,putDouble,putChar,putObject等方法,直接修改内存数据(可以越过访问权限)

  24. unsafe.putObject(data,fieldOffset,"这是新的值");

  25. System.out.println(data.getName());

  26.  
  27.  
  28. /**

  29. * 我们可以在运行时创建一个类,比如从已编译的.class文件中。将类内容读取为字节数组,

  30. * 并正确地传递给defineClass方法;当你必须动态创建类,而现有代码中有一些代理, 这是很有用的

  31. */

  32. File file = new File("C:\\workspace\\idea2\\disruptor\\target\\classes\\com\\onyx\\distruptor\\test\\Data.class");

  33. FileInputStream input = new FileInputStream(file);

  34. byte[] content = new byte[(int)file.length()];

  35. input.read(content);

  36. Class c = unsafe.defineClass(null, content, 0, content.length,null,null);

  37. c.getMethod("getId").invoke(c.newInstance(), null);

  38.  
  39.  
  40.  
  41. /**

  42. * 内存操作

  43. * 可以在Java内存区域中分配内存(allocateMemory),设置内存(setMemory,用于初始化),

  44. * 在指定的内存位置中设置值(putInt\putBoolean\putDouble等基本类型)

  45. */

  46. //分配一个8byte的内存

  47. long address = unsafe.allocateMemory(8L);

  48. //初始化内存填充1

  49. unsafe.setMemory(address, 8L, (byte) 1);

  50. //测试输出

  51. System.out.println("add byte to memory:" + unsafe.getInt(address));

  52. //设置0-3 4个byte为0x7fffffff

  53. unsafe.putInt(address, 0x7fffffff);

  54. //设置4-7 4个byte为0x80000000

  55. unsafe.putInt(address + 4, 0x80000000);

  56. //int占用4byte

  57. System.out.println("add byte to memory:" + unsafe.getInt(address));

  58. System.out.println("add byte to memory:" + unsafe.getInt(address + 4));

 

 
  1. /**

  2. * CAS操作

  3. * Compare And Swap(比较并交换),当需要改变的值为期望的值时,那么就替换它为新的值,是原子

  4. * (不可在分割)的操作。很多并发框架底层都用到了CAS操作,CAS操作优势是无锁,可以减少线程切换耗费

  5. * 的时间,但CAS经常失败运行容易引起性能问题,也存在ABA问题。在Unsafe中包含compareAndSwapObject、

  6. * compareAndSwapInt、compareAndSwapLong三个方法,compareAndSwapInt的简单示例如下。

  7. */

  8. Data data = new Data();

  9. data.setId(1L);

  10. Field id = data.getClass().getDeclaredField("id");

  11. long l = unsafe.objectFieldOffset(id);

  12. id.setAccessible(true);

  13. //比较并交换,比如id的值如果是所期望的值1,那么就替换为2,否则不做处理

  14. unsafe.compareAndSwapLong(data,1L,1L,2L);

  15. System.out.println(data.getId());

 

 
  1. /**

  2. * 常量获取

  3. *

  4. * 可以获取地址大小(addressSize),页大小(pageSize),基本类型数组的偏移量

  5. * (Unsafe.ARRAY_INT_BASE_OFFSET\Unsafe.ARRAY_BOOLEAN_BASE_OFFSET等)、

  6. * 基本类型数组内元素的间隔(Unsafe.ARRAY_INT_INDEX_SCALE\Unsafe.ARRAY_BOOLEAN_INDEX_SCALE等)

  7. */

  8. //get os address size

  9. System.out.println("address size is :" + unsafe.addressSize());

  10. //get os page size

  11. System.out.println("page size is :" + unsafe.pageSize());

  12. //int array base offset

  13. System.out.println("unsafe array int base offset:" + Unsafe.ARRAY_INT_BASE_OFFSET);

  14.  
  15.  
  16.  
  17. /**

  18. * 线程许可

  19. * 许可线程通过(park),或者让线程等待许可(unpark),

  20. */

  21. Thread packThread = new Thread(() -> {

  22. long startTime = System.currentTimeMillis();

  23. //纳秒,相对时间park

  24. unsafe.park(false,3000000000L);

  25. //毫秒,绝对时间park

  26. //unsafe.park(true,System.currentTimeMillis()+3000);

  27.  
  28. System.out.println("main thread end,cost :"+(System.currentTimeMillis()-startTime)+"ms");

  29. });

  30. packThread.start();

  31. TimeUnit.SECONDS.sleep(1);

  32. //注释掉下一行后,线程3秒数后进行输出,否则在1秒后输出

  33. unsafe.unpark(packThread);

 

 
  1. /**

  2. * Java数组大小的最大值为Integer.MAX_VALUE。使用直接内存分配,我们创建的数组大小受限于堆大小;

  3. * 实际上,这是堆外内存(off-heap memory)技术,在java.nio包中部分可用;

  4. *

  5. * 这种方式的内存分配不在堆上,且不受GC管理,所以必须小心Unsafe.freeMemory()的使用。

  6. * 它也不执行任何边界检查,所以任何非法访问可能会导致JVM崩溃

  7. */

  8. public class SuperArray {

  9.  
  10. private static Unsafe unsafe = null;

  11. private static Field getUnsafe = null;

  12.  
  13. static {

  14. try {

  15. getUnsafe = Unsafe.class.getDeclaredField("theUnsafe");

  16. getUnsafe.setAccessible(true);

  17. unsafe = (Unsafe) getUnsafe.get(null);

  18. } catch (NoSuchFieldException e) {

  19. e.printStackTrace();

  20. } catch (IllegalAccessException e) {

  21. e.printStackTrace();

  22. }

  23. }

  24.  
  25.  
  26. private final static int BYTE = 1;

  27.  
  28. private long size;

  29. private long address;

  30.  
  31. public SuperArray(long size) {

  32. this.size = size;

  33. address = unsafe.allocateMemory(size * BYTE);

  34. }

  35.  
  36. public void set(long i, byte value) {

  37. unsafe.putByte(address + i * BYTE, value);

  38. }

  39.  
  40. public int get(long idx) {

  41. return unsafe.getByte(address + idx * BYTE);

  42. }

  43.  
  44. public long size() {

  45. return size;

  46. }

  47.  
  48.  
  49. public static void main(String[] args) {

  50. long SUPER_SIZE = (long)Integer.MAX_VALUE * 2;

  51. SuperArray array = new SuperArray(SUPER_SIZE);

  52. System.out.println("Array size:" + array.size()); // 4294967294

  53. int sum=0;

  54. for (int i = 0; i < 100; i++) {

  55. array.set((long)Integer.MAX_VALUE + i, (byte)3);

  56. sum += array.get((long)Integer.MAX_VALUE + i);

  57. }

  58. System.out.println(sum);

  59. }

  60.  
  61.  
  62.  
  63. }

如果您觉得写得不多, 可以请作者喝一杯咖啡

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值