我是靠谱客的博主 笑点低板栗,最近开发中收集的这篇文章主要介绍sun.misc.Unsafed的API说明sun.misc.Unsafe,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • sun.misc.Unsafe
    • API分类
      • 内存管理
      • 内存操作
      • 内存屏障
      • 通过内存偏移地址修改变量值
        • CAS操作
      • 线程的挂起与唤醒
    • 实操
    • 参考文档

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 BarriersLoad1; LoadLoad; Load2确保 Load1 数据的装载,之前于 Load2 及所有后续装载指令的装载。
StoreStore BarriersStore1; StoreStore; Store2确保 Store1 数据对其他处理器可见(刷新到内存),之前于 Store2 及所有后续存储指令的存储。
LoadStore BarriersLoad1; LoadStore; Store2确保 Load1 数据装载,之前于 Store2 及所有后续的存储指令刷新到内存。
StoreLoad BarriersStore1; 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类 (内存操作/对象字段操作/原子操作/线程操作)


参考文档

  • Java Magic. Part 4: sun.misc.Unsafe
  • Java魔法类:sun.misc.Unsafe
  • sun.misc.unsafe类的使用
  • sun.misc.Unsafe操作手册

最后

以上就是笑点低板栗为你收集整理的sun.misc.Unsafed的API说明sun.misc.Unsafe的全部内容,希望文章能够帮你解决sun.misc.Unsafed的API说明sun.misc.Unsafe所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(38)

评论列表共有 0 条评论

立即
投稿
返回
顶部