ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java:Unsafe类的使用

2022-01-05 21:34:40  阅读:169  来源: 互联网

标签:Java getUnsafe Unsafe long NUM private 使用 public


一、获取Unsafe类实例

1     Field field = Unsafe.class.getDeclaredField("theUnsafe");
2     field.setAccessible(true);
3     return (Unsafe) field.get(null);    

 theUnsafe 是Unsafe内部的一个字段,Unsafe在静态代码块中实例化一个Unsafe类,并通过反射获取Unsafe。

还有一个是Unsafe.getUnsafe静态方法也是获取Unsafe实例的,不过这个getUnsafe方法会检查类加载器类型,如果非Bootstrap Classloader就会抛SecurityException异常。

 二、使用Unsafe来实例化一个类

 1 class InitializationOrdering {
 2     private long a;
 3 
 4     public InitializationOrdering() {
 5         this.a = 1;
 6     }
 7 
 8     public long getA() {
 9         return this.a;
10     }
11 }

使用 new 构造函数初始化该对象时, getA() 方法将返回值1,可以使用Unsafe使用 allocateInstance() 方法。它只会为我们的类分配内存,不会调用构造函数:

 1         InitializationOrdering o3 = null;
 2         try {
 3             o3 = (InitializationOrdering) THE_UNSAFE.allocateInstance(InitializationOrdering.class);
 4             // 构造函数没有被调用, 因此, getA()方法返回了long类型的默认值——0
 5             if (o3.getA() == 0) {
 6                 System.out.println("不会调用InitializationOrdering构造函数获取到的a为0");
 7             }
 8         } catch (InstantiationException e) {
 9             e.printStackTrace();
10         }

三、改变私有字段

1 class SecretHolder {
2     private int SECRET_VALUE = 0;
3 
4     public boolean secretIsDisclosed() {
5         return SECRET_VALUE == 1;
6     }
7 }

通过Unsafe类的 putInt() 方法修改SecretHolder的私有整形字段。

 1         SecretHolder secretHolder = new SecretHolder();
 2         try {
 3             Field f = secretHolder.getClass().getDeclaredField("SECRET_VALUE");
 4 
 5             THE_UNSAFE.putInt(secretHolder, THE_UNSAFE.objectFieldOffset(f), 1);
 6 
 7             if(secretHolder.secretIsDisclosed()){
 8                 System.out.println("Unsafe改变了私有字段的值, 改成了1");
 9             }
10         } catch (NoSuchFieldException e) {
11             e.printStackTrace();
12         }

四、抛出异常

1         // 抛出异常, 使用throwException()方法抛出任何异常而不限制调用者处理该异常
2         THE_UNSAFE.throwException(new IOException());

五、堆外内存

应用程序在 JVM 上的可用内存不足,我们最终可能会迫使 GC 进程过于频繁地运行。想在GC时减少回收停顿对于应用的影响,我们需要一个特殊的内存区域,在堆外并且不受 GC 进程控制。

Unsafe的 allocateMemory() 方法能够让我们能够在堆外给一个比较大的对象分配内存,这代表这这些内存不会GC和JVM看到或者纳入它们的管理,而是由操作系统进行管理的。对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存,比如I/O通信过程。

 1 class OffHeapArray {
 2     private final static int BYTE = 1;
 3     private long size;
 4     private long address;
 5 
 6     public OffHeapArray(long size) throws NoSuchFieldException, IllegalAccessException {
 7         this.size = size;
 8         address = getUnsafe().allocateMemory(size * BYTE);
 9     }
10 
11     private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
12         Field f = Unsafe.class.getDeclaredField("theUnsafe");
13         f.setAccessible(true);
14         return (Unsafe) f.get(null);
15     }
16 
17     public void set(long i, byte value) throws NoSuchFieldException, IllegalAccessException {
18         getUnsafe().putByte(address + i * BYTE, value);
19     }
20 
21     public int get(long idx) throws NoSuchFieldException, IllegalAccessException {
22         return getUnsafe().getByte(address + idx * BYTE);
23     }
24 
25     public long size() {
26         return size;
27     }
28 
29     public void freeMemory() throws NoSuchFieldException, IllegalAccessException {
30         getUnsafe().freeMemory(address);
31     }
32 }

使用 allocateMemory 分配内存,并往分配的内存中 putByte 进去一个3(转byte),并且 getByte 循环100次并求和,最后看sum结果是否为300.。

 1         long SUPER_SIZE = (long) Integer.MAX_VALUE * 2;
 2         try {
 3             OffHeapArray array = new OffHeapArray(SUPER_SIZE);
 4 
 5             int sum = 0;
 6             // 将 N 个字节值放入这个数组,然后检索这些值,将它们相加以测试我们的寻址是否正常工作
 7             for (int i = 0; i < 100; i++) {
 8                 array.set((long) Integer.MAX_VALUE + i, (byte) 3);
 9                 sum += array.get((long) Integer.MAX_VALUE + i);
10             }
11             if (array.size() == SUPER_SIZE) {
12                 System.out.println("数组大小为SUPER_SIZE");
13             }
14 
15             if (sum == 300) {
16                 System.out.println("数组总和为300");
17             }
18         } catch (NoSuchFieldException | IllegalAccessException e) {
19             e.printStackTrace();
20         }

六、ComparedAndSwap操作

 1 class CASCount {
 2     private Unsafe unsafe;
 3     private volatile long counter = 0;
 4     private long offset;
 5 
 6     private Unsafe getUnsafe() throws IllegalAccessException, NoSuchFieldException {
 7         Field f = Unsafe.class.getDeclaredField("theUnsafe");
 8         f.setAccessible(true);
 9         return (Unsafe) f.get(null);
10     }
11 
12     CASCount() throws Exception {
13         unsafe = getUnsafe();
14         offset = unsafe.objectFieldOffset(CASCount.class.getDeclaredField("counter"));
15     }
16 
17     /**
18      * 使用Unsafe中的CompareAndSwapLong方法构建基于CAS计数器
19      */
20     public void increment() {
21         long before = counter;
22         while (!unsafe.compareAndSwapLong(this, offset, before, before + 1)) {
23             before = counter;
24         }
25     }
26 
27     public long getCounter() {
28         return counter;
29     }
30 }

获取计数器字段的地址,以便稍后在 increment 方法中使用它,在while循环中使用 compareAndSwapLong 来增加先前获取的值,可以通过增加多个线程的共享计数器来测试。

 1         int NUM_OF_THREAD = 1_10;
 2         int NUM_OF_INCREMENT = 10_100;
 3         ExecutorService service = Executors.newFixedThreadPool(NUM_OF_THREAD);
 4 
 5         try {
 6             CASCount casCount = null;
 7             casCount = new CASCount();
 8             CASCount finalCasCount = casCount;
 9             IntStream.rangeClosed(0, NUM_OF_THREAD - 1)
10                     .forEach(i -> service.submit(() -> IntStream
11                             .rangeClosed(0, NUM_OF_INCREMENT - 1)
12                             .forEach(j -> finalCasCount.increment())));
13             System.out.println("NUM_OF_THREAD:" + NUM_OF_THREAD);
14             System.out.println("NUM_OF_INCREMENT:"+ NUM_OF_INCREMENT);
15             System.out.println(NUM_OF_THREAD * NUM_OF_INCREMENT == casCount.getCounter());
16         } catch (Exception e) {
17             e.printStackTrace();
18         }

七、Park & Unpark

Unsafe API 中有两种的方法,JVM 使用它们来上下文切换线程。当线程正在等待某个动作时,JVM 可以使用Unsafe类中的 park() 方法使该线程阻塞。它与 Object.wait() 方法非常相似,但它调用本机操作系统代码,从而利用一些架构细节来获得最佳性能。当线程被阻塞并需要再次运行时,JVM 使用 unpark() 方法。

 

标签:Java,getUnsafe,Unsafe,long,NUM,private,使用,public
来源: https://www.cnblogs.com/magic-sea/p/15395389.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有