sun.misc.Unsafed的API说明
Posted 顧棟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sun.misc.Unsafed的API说明相关的知识,希望对你有一定的参考价值。
文章目录
sun.misc.Unsafe
在JDK11中,已经将Unsafe已经剔除了,取而代之就是JDK9中就开始引入的Variable Handles
,详细的介绍可以参考 http://openjdk.java.net/jeps/193
由于大部分的程序依旧运行在JDK8上,所有还是收集了一下这个类的相关信息。
sun.misc.Unsafe 基于堆的字节数组的内存操作,简单来说可以用来在任意内存地址位置处读写数据,可见,对于普通用户来说,使用起来还是比较危险的。是不建议使用的,所以这边只会去介绍一些api的作用。源码采用jdk1.8.0_271
。
API分类
Unsafe API的大部分方法都是native实现,主要包括以下几类:
(1)Info相关。主要返回某些低级别的内存信息:addressSize(), pageSize()
(2)Objects相关。主要提供Object和它的域操纵方法:allocateInstance(),objectFieldOffset()
(3)Class相关。主要提供Class和它的静态域操纵方法:staticFieldOffset(),defineClass(),defineAnonymousClass(),ensureClassInitialized()
(4)Arrays相关。数组操纵方法:arrayBaseOffset(),arrayIndexScale()
(5)Synchronization相关。主要提供低级别同步原语(如基于CPU的CAS(Compare-And-Swap)原语):monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt()
(6)Memory相关。直接内存访问方法(绕过JVM堆直接操纵本地内存):allocateMemory(),copyMemory(),freeMemory(),getAddress(),getInt(),putInt()
内存管理
public native long getAddress(long var1);
public native void putAddress(long var1, long var3);
//开辟一块内存. 参数为要开辟内存的大小, 多少字节; 返回值是一个native pointer, 就是内存地址.
public native long allocateMemory(long var1);
// 重新分配 (扩展) 一块内存 (之前分配内存当然会被GC回收掉). 第一个参数为原内存地址, 第二个参数为重新分配的内存的大小 (要超过前一块内存的大小), 返回新的内存地址. 原内存中的内容会迁移到新开辟的内存中.
public native long reallocateMemory(long var1, long var3);
public native void setMemory(Object var1, long var2, long var4, byte var6);
public void setMemory(long var1, long var3, byte var5)
this.setMemory((Object)null, var1, var3, var5);
// 内存数据拷贝
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
public void copyMemory(long var1, long var3, long var5)
this.copyMemory((Object)null, var1, (Object)null, var3, var5);
// 释放一块内存. 参数为内存地址. 即allocateMemory()方法的返回值.
public native void freeMemory(long var1);
内存操作
// 获取实例字段的偏移量,在这个类的其他方法中这个值只是被用作一个访问特定field的一个方式。这个值对于给定的field是唯一的,并且后续对该方法的调用都应该返回相同的值。
public native long objectFieldOffset(Field var1);
// 获取静态字段的偏移量
public native long staticFieldOffset(Field var1);
// 获取给定数组中第一个元素的偏移地址。为了存取数组中的元素,这个偏移地址与arrayIndexScale方法的非0返回值一起被使用。
public native int arrayBaseOffset(Class<?> var1);
// 获取用户给定数组寻址的换算因子。如果不能返回一个合适的换算因子的时候就会返回0。这个返回值能够与arrayBaseOffset一起使用去存取这个数组class中的元素
public native int arrayIndexScale(Class<?> var1);
内存屏障
这里的解释主要参考JDK9里面的
// 对应CPU的LoadLoad+LoadStore
public native void loadFence();
// 对应CPU的StoreStore+LoadStore
public native void storeFence();
// 对应CPU的loadFence+storeFence+StoreLoad
public native void fullFence();
四种内存屏障表
屏障类型 | 指令示例 | 说明 |
---|---|---|
LoadLoad Barriers | Load1; LoadLoad; Load2 | 确保 Load1 数据的装载,之前于 Load2 及所有后续装载指令的装载。 |
StoreStore Barriers | Store1; StoreStore; Store2 | 确保 Store1 数据对其他处理器可见(刷新到内存),之前于 Store2 及所有后续存储指令的存储。 |
LoadStore Barriers | Load1; LoadStore; Store2 | 确保 Load1 数据装载,之前于 Store2 及所有后续的存储指令刷新到内存。 |
StoreLoad Barriers | Store1; StoreLoad; Load2 | 确保 Store1 数据对其他处理器变得可见(指刷新到内存),之前于 Load2 及所有后续装载指令的装载。 |
通过内存偏移地址修改变量值
原子操作 => 值操作: 获取 和 更新
public final int getAndAddInt(Object var1, long var2, int var4)
int var5;
do
var5 = this.getIntVolatile(var1, var2);
while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
public final long getAndAddLong(Object var1, long var2, long var4)
long var6;
do
var6 = this.getLongVolatile(var1, var2);
while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
public final int getAndSetInt(Object var1, long var2, int var4)
int var5;
do
var5 = this.getIntVolatile(var1, var2);
while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
public final long getAndSetLong(Object var1, long var2, long var4)
long var6;
do
var6 = this.getLongVolatile(var1, var2);
while(!this.compareAndSwapLong(var1, var2, var6, var4));
return var6;
public final Object getAndSetObject(Object var1, long var2, Object var4)
Object var5;
do
var5 = this.getObjectVolatile(var1, var2);
while(!this.compareAndSwapObject(var1, var2, var5, var4));
return var5;
getLongVolatile/putLongVolatile等等方法 使用volatile语义去存取数据
CAS操作
/*
*对象的字段进行CAS操作
*@param var1 对象
*@param var2 字段的内存地址偏移量
*@param var4 旧值,此时期待对象的字段的值
*@param var5 新值,如果字段现在的值就是var4那么更新成var5
*/
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
线程的挂起与唤醒
// 唤醒线程
public native void unpark(Object var1);
// 阻塞线程 若var2=0时,若不出现upark和中断,就永不唤醒;若var2>0就是deadline time;
// var1为true var2就是从1970年到deadline时间的毫秒数
// var1为false var2就是超时前的纳秒数。
public native void park(boolean var1, long var2);
实操
如果想做实验的话 ,可以参考一下文章:sun.misc.Unsafe类 (内存操作/对象字段操作/原子操作/线程操作)
参考文档
以上是关于sun.misc.Unsafed的API说明的主要内容,如果未能解决你的问题,请参考以下文章