本文章来源于:https://github.com/Zeb-D/my-review ,请star 强力支持,你的支持,就是我的动力。

[TOC]

背景

平时在啃并发库会发现一些底层的影子,虽然J.U.C并发库 各个类有不同的逻辑,但具体行为支持还是得靠sun.misc.Unsafe,这个类威力无穷;

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。

注:本文对sun.misc.Unsafe公共API功能及相关应用场景进行介绍。

基本介绍

如下Unsafe源码所示,Unsafe类为一单例实现,提供静态方法getUnsafe获取Unsafe实例,当且仅当调用getUnsafe方法的类为引导类加载器所加载时才合法,否则抛出SecurityException异常。

public final class Unsafe {// 单例对象private static final Unsafe theUnsafe;private Unsafe() {}@CallerSensitivepublic static Unsafe getUnsafe() {Class var0 = Reflection.getCallerClass();// 仅在引导类加载器`BootstrapClassLoader`加载时才合法if(!VM.isSystemDomainLoader(var0.getClassLoader())) {    throw new SecurityException("Unsafe");} else {return theUnsafe;}}
}

那如若想使用这个类,该如何获取其实例?有如下两个可行方案。

其一,从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。

java -Xbootclasspath/a: ${path}   // 其中path为调用Unsafe相关方法的类所在jar包路径 

其二,通过反射获取单例对象theUnsafe。

private static Unsafe reflectGetUnsafe() {try {Field field = Unsafe.class.getDeclaredField("theUnsafe");field.setAccessible(true);return (Unsafe) field.get(null);} catch (Exception e) {log.error(e.getMessage(), e);return null;}
}

功能介绍

如上图所示,Unsafe提供的API大致可分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类,下面将对其相关方法和应用场景进行详细介绍。

内存操作

这部分主要包含堆外内存的分配、拷贝、释放、给定地址值操作等方法。

//分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
//扩充内存
public native long reallocateMemory(long address, long bytes);
//释放内存
public native void freeMemory(long address);
//在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);

通常,我们在Java中创建的对象都处于堆内内存(heap)中,堆内内存是由JVM所管控的Java进程内存,并且它们遵循JVM的内存管理机制,JVM会采用垃圾回收机制统一管理堆内存。与之相对的是堆外内存,存在于JVM管控之外的内存区域,Java中对堆外内存的操作,依赖于Unsafe提供的操作堆外内存的native方法。

使用堆外内存的原因
  • 对垃圾回收停顿的改善。由于堆外内存是直接受操作系统管理而不是JVM,所以当我们使用堆外内存时,即可保持较小的堆内内存规模。从而在GC时减少回收停顿对于应用的影响。
  • 提升程序I/O操作的性能。通常在I/O通信过程中,会存在堆内内存到堆外内存的数据拷贝操作,对于需要频繁进行内存间数据拷贝且生命周期较短的暂存数据,都建议存储到堆外内存。
典型应用

DirectByteBuffer是Java用于实现堆外内存的一个重要类,通常用在通信过程中做缓冲池,如在Netty、MINA等NIO框架中应用广泛。DirectByteBuffer对于堆外内存的创建、使用、销毁等逻辑均由Unsafe提供的堆外内存API来实现。

下图为DirectByteBuffer构造函数,创建DirectByteBuffer的时候,通过Unsafe.allocateMemory分配内存、Unsafe.setMemory进行内存初始化,而后构建Cleaner对象用于跟踪DirectByteBuffer对象的垃圾回收,以实现当DirectByteBuffer被垃圾回收时,分配的堆外内存一起被释放。

// Primary constructor//DirectByteBuffer(int cap) {                   // package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);long base = 0;try {// 分配内存,返回基地址base = unsafe.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}// 内存初始化unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}// 跟踪DirectByteBuffer对象垃圾回收,以实现堆外垃圾回收cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}

那么如何通过构建垃圾回收追踪对象Cleaner实现堆外内存释放呢?

Cleaner继承自Java四大引用类型之一的虚引用PhantomReference(众所周知,无法通过虚引用获取与之关联的对象实例,且当对象仅被虚引用引用时,在任何发生GC的时候,其均可被回收),通常PhantomReference与引用队列ReferenceQueue结合使用,可以实现虚引用关联对象被垃圾回收时能够进行系统通知、资源清理等功能。如下图所示,当某个被Cleaner引用的对象将被回收时,JVM垃圾收集器会将此对象的引用放入到对象引用中的pending链表中,等待Reference-Handler进行相关处理。其中,Reference-Handler为一个拥有最高优先级的守护线程,会循环不断的处理pending链表中的对象引用,执行Cleaner的clean方法进行相关清理工作。

所以当DirectByteBuffer仅被Cleaner引用(即为虚引用)时,其可以在任意GC时段被回收。当DirectByteBuffer实例对象被回收时,在Reference-Handler线程操作中,会调用Cleaner的clean方法根据创建Cleaner时传入的Deallocator来进行堆外内存的释放。

//java.lang.ref.Reference#tryHandlePending
static boolean tryHandlePending(boolean waitForNotify) {Reference<Object> r;Cleaner c;try {synchronized (lock) {if (pending != null) {r = pending;// 'instanceof' might throw OutOfMemoryError sometimes// so do this before un-linking 'r' from the 'pending' chain...c = r instanceof Cleaner ? (Cleaner) r : null;// unlink 'r' from 'pending' chainpending = r.discovered;r.discovered = null;} else {// The waiting on the lock may cause an OutOfMemoryError// because it may try to allocate exception objects.if (waitForNotify) {lock.wait();}// retry if waitedreturn waitForNotify;}}} catch (OutOfMemoryError x) {// Give other threads CPU time so they hopefully drop some live references// and GC reclaims some space.// Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above// persistently throws OOME for some time...Thread.yield();// retryreturn true;} catch (InterruptedException x) {// retryreturn true;}// Fast path for cleanersif (c != null) {// c为Cleaner实例,则调用clean方法执行具体清理动作c.clean();return true;}//非Cleaner实例,则放入引用队列ReferenceQueue<? super Object> q = r.queue;if (q != ReferenceQueue.NULL) q.enqueue(r);return true;
}

//java.nio.DirectByteBuffer.Deallocator
private static class Deallocatorimplements Runnable{private static Unsafe unsafe = Unsafe.getUnsafe();private long address;private long size;private int capacity;private Deallocator(long address, long size, int capacity) {assert (address != 0);this.address = address;this.size = size;this.capacity = capacity;}public void run() {if (address == 0) {// Paranoiareturn;}unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);//释放内存}}

CAS相关

如下源代码释义所示,这部分主要为CAS相关操作的方法。

/***  CAS* @param o         包含要修改field的对象* @param offset    对象中某field的偏移量* @param expected  期望值* @param update    更新值* @return          true | false*/
public final native boolean compareAndSwapObject(Object o, long offset,  Object expected, Object update);public final native boolean compareAndSwapInt(Object o, long offset, int expected,int update);public final native boolean compareAndSwapLong(Object o, long offset, long expected, long update);

什么是CAS? 即比较并替换,实现并发算法时常用到的一种技术。CAS操作包含三个操作数——内存位置、预期原值及新值。执行CAS操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。我们都知道,CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe提供的CAS方法(如compareAndSwapXXX)底层实现即为CPU指令cmpxchg。

典型应用

CAS在java.util.concurrent.atomic相关类、Java AQS、CurrentHashMap等实现上有非常广泛的应用。如下图所示,AtomicInteger的实现中,静态字段valueOffset即为字段value的内存偏移地址,valueOffset的值在AtomicInteger初始化时,在静态代码块中通过Unsafe的objectFieldOffset方法获取。在AtomicInteger中提供的线程安全方法中,通过字段valueOffset的值可以定位到AtomicInteger对象中value的内存地址,从而可以根据CAS实现对value字段的原子操作。

public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;....

某个AtomicInteger对象自增操作前后的内存示意图,对象的基地址baseAddress=“0x110000”,通过baseAddress+valueOffset得到value的内存地址valueAddress=“0x11000c”;然后通过CAS进行原子性的更新操作,成功则返回,否则继续重试,直到更新成功为止。

线程调度

这部分,包括线程挂起、恢复、锁机制等方法。

//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);

如上源码说明中,方法park、unpark即可实现线程的挂起与恢复,将一个线程进行挂起是通过park方法实现的,调用park方法后,线程将一直阻塞直到超时或者中断等条件出现;unpark可以终止一个挂起的线程,使其恢复正常。

典型应用

Java锁和同步器框架的核心类AbstractQueuedSynchronizer,就是通过调用LockSupport.park()LockSupport.unpark()实现线程的阻塞和唤醒的,而LockSupport的park、unpark方法实际是调用Unsafe的park、unpark方式来实现。

Class相关

此部分主要提供Class和它的静态字段的操作相关方法,包含静态字段内存定位、定义类、定义匿名类、检验&确保初始化等。

//获取给定静态字段的内存地址偏移量,这个值对于给定的字段是唯一且固定不变的
public native long staticFieldOffset(Field f);
//获取一个静态类中给定字段的对象指针
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false。
public native boolean shouldBeInitialized(Class<?> c);
//检测给定的类是否已经初始化。通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。
public native void ensureClassInitialized(Class<?> c);
//定义一个类,此方法会跳过JVM的所有安全检查,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
public native Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
//定义一个匿名类
public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches);

典型应用

从Java 8开始,JDK使用invokedynamic及VM Anonymous Class结合来实现Java语言层面上的Lambda表达式。

  • invokedynamic: invokedynamic是Java 7为了实现在JVM上运行动态语言而引入的一条新的虚拟机指令,它可以实现在运行期动态解析出调用点限定符所引用的方法,然后再执行该方法,invokedynamic指令的分派逻辑是由用户设定的引导方法决定。
  • VM Anonymous Class:可以看做是一种模板机制,针对于程序动态生成很多结构相同、仅若干常量不同的类时,可以先创建包含常量占位符的模板类,而后通过Unsafe.defineAnonymousClass方法定义具体类时填充模板的占位符生成具体的匿名类。生成的匿名类不显式挂在任何ClassLoader下面,只要当该类没有存在的实例对象、且没有强引用来引用该类的Class对象时,该类就会被GC回收。故而VM Anonymous Class相比于Java语言层面的匿名内部类无需通过ClassClassLoader进行类加载且更易回收。

在Lambda表达式实现中,通过invokedynamic指令调用引导方法生成调用点,在此过程中,会通过ASM动态生成字节码,而后利用Unsafe的defineAnonymousClass方法定义实现相应的函数式接口的匿名类,然后再实例化此匿名类,并返回与此匿名类中函数式方法的方法句柄关联的调用点;而后可以通过此调用点实现调用相应Lambda表达式定义逻辑的功能。下面以如下图所示的Test类来举例说明。

/*** 在Lambda表达式实现中,通过invokedynamic指令调用引导方法生成调用点,* 在此过程中,会通过ASM动态生成字节码,* 而后利用Unsafe的defineAnonymousClass方法定义实现相应的函数式接口的匿名类,* 然后再实例化此匿名类,并返回与此匿名类中函数式方法的方法句柄关联的调用点;* 而后可以通过此调用点实现调用相应Lambda表达式定义逻辑的功能。** @author created by Zeb灬D on 2020-02-17 11:48*/
public class JavapTest {public static void main(String[] args) {Consumer<String> consumer = s -> System.out.println("s");consumer.accept("Lambda");}
}

查看字节码javap -verbose /Users/xxx/my-java-api/learn-java/target/classes/com/yd/java/java8/JavapTest.class 重点代码如下:

{public com.yd.java.java8.JavapTest();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 14: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcom/yd/java/java8/JavapTest;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: invokedynamic #2,  0              // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;5: astore_16: aload_17: ldc           #3                  // String Lambda9: invokeinterface #4,  2            // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V14: returnLineNumberTable:line 16: 0line 17: 6line 18: 14LocalVariableTable:Start  Length  Slot  Name   Signature0      15     0  args   [Ljava/lang/String;6       9     1 consumer   Ljava/util/function/Consumer;LocalVariableTypeTable:Start  Length  Slot  Name   Signature6       9     1 consumer   Ljava/util/function/Consumer<Ljava/lang/String;>;
}
SourceFile: "JavapTest.java"
InnerClasses:public static final #64= #63 of #67; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:0: #33 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;Method arguments:#34 (Ljava/lang/Object;)V#35 invokestatic com/yd/java/java8/JavapTest.lambda$main$0:(Ljava/lang/String;)V#36 (Ljava/lang/String;)V

对象操作

此部分主要包含对象成员属性相关操作及非常规的对象实例化方式等相关方法。

//返回对象成员属性在内存地址相对于此对象的内存地址的偏移量
public native long objectFieldOffset(Field f);
//获得给定对象的指定地址偏移量的值,与此类似操作还有:getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//给定对象的指定地址偏移量设值,与此类似操作还有:putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
public native void putObjectVolatile(Object o, long offset, Object x);
//有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
public native void putOrderedObject(Object o, long offset, Object x);
//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

典型应用
  • 常规对象实例化方式:我们通常所用到的创建对象的方式,从本质上来讲,都是通过new机制来实现对象的创建。但是,new机制有个特点就是当类只提供有参的构造函数且无显示声明无参构造函数时,则必须使用有参构造函数进行对象构造,而使用有参构造函数时,必须传递相应个数的参数才能完成对象实例化。
  • 非常规的实例化方式:而Unsafe中提供allocateInstance方法,仅通过Class对象就可以创建此类的实例对象,而且不需要调用其构造函数、初始化代码、JVM安全检查等。它抑制修饰符检测,也就是即使构造器是private修饰的也能通过此方法实例化,只需提类对象即可创建相应的对象。由于这种特性,allocateInstance在java.lang.invoke、Objenesis(提供绕过类构造器的对象生成方式)、Gson(反序列化时用到)中都有相应的应用。

如下图所示,在Gson反序列化时,如果类有默认构造函数,则通过反射调用默认构造函数创建实例,否则通过UnsafeAllocator来实现对象实例的构造,UnsafeAllocator通过调用Unsafe的allocateInstance实现对象的实例化,保证在目标类无默认构造函数时,反序列化不够影响。

//com.google.gson.internal.UnsafeAllocator#create
public static UnsafeAllocator create() {try {Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");Field f = unsafeClass.getDeclaredField("theUnsafe");f.setAccessible(true);final Object unsafe = f.get((Object)null);// 反射获取到allocateInstance 方法final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);

数组相关

这部分主要介绍与数据操作相关的arrayBaseOffset与arrayIndexScale这两个方法,两者配合起来使用,即可定位数组中每个元素在内存中的位置。

//返回数组中第一个元素的偏移地址
public native int arrayBaseOffset(Class<?> arrayClass);
//返回数组中一个元素占用的大小
public native int arrayIndexScale(Class<?> arrayClass);

典型应用

这两个与数据操作相关的方法,在java.util.concurrent.atomic 包下的AtomicIntegerArray(可以实现对Integer数组中每个元素的原子性操作)中有典型的应用,如下图AtomicIntegerArray源码所示,通过Unsafe的arrayBaseOffset、arrayIndexScale分别获取数组首元素的偏移地址base及单个元素大小因子scale。后续相关原子性操作,均依赖于这两个值进行数组中元素的定位,如下图二所示的getAndAdd方法即通过checkedByteOffset方法获取某数组元素的偏移地址,而后通过CAS实现原子性操作。

public class AtomicIntegerArray implements java.io.Serializable {private static final long serialVersionUID = 2862133569453604235L;private static final Unsafe unsafe = Unsafe.getUnsafe();// 获取数组元素首地址private static final int base = unsafe.arrayBaseOffset(int[].class);private static final int shift;private final int[] array;static {// 获取每个元素所占的大小int scale = unsafe.arrayIndexScale(int[].class);if ((scale & (scale - 1)) != 0)throw new Error("data type scale not a power of two");shift = 31 - Integer.numberOfLeadingZeros(scale);}// 通过数据元素的位置计算偏移地址private static long byteOffset(int i) {return ((long) i << shift) + base;}public final int getAndAdd(int i, int delta) {return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);}

内存屏障

在Java 8中引入,用于定义内存屏障(也称内存栅栏,内存栅障,屏障指令等,是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作),避免代码重排序。

//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
public native void loadFence();
//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();

典型应用

在Java 8中引入了一种锁的新机制——StampedLock,它可以看成是读写锁的一个改进版本。StampedLock提供了一种乐观读锁的实现,这种乐观读锁类似于无锁的操作,完全不会阻塞写线程获取写锁,从而缓解读多写少时写线程“饥饿”现象。由于StampedLock提供的乐观读锁不阻塞写线程获取读锁,当线程共享变量从主内存load到线程工作内存时,会存在数据不一致问题,所以当使用StampedLock的乐观读锁时,需要遵从如下图用例中使用的模式来确保数据的一致性。

如上图用例所示计算坐标点Point对象,包含点移动方法move及计算此点到原点的距离的方法distanceFromOrigin。在方法distanceFromOrigin中,首先,通过tryOptimisticRead方法获取乐观读标记;然后从主内存中加载点的坐标值 (x,y);而后通过StampedLock的validate方法校验锁状态,判断坐标点(x,y)从主内存加载到线程工作内存过程中,主内存的值是否已被其他线程通过move方法修改,如果validate返回值为true,证明(x, y)的值未被修改,可参与后续计算;否则,需加悲观读锁,再次从主内存加载(x,y)的最新值,然后再进行距离计算。其中,校验锁状态这步操作至关重要,需要判断锁状态是否发生改变,从而判断之前copy到线程工作内存中的值是否与主内存的值存在不一致。

下面为StampedLock.validate方法的源码实现,通过锁标记与相关常量进行位运算、比较来校验锁状态,在校验逻辑之前,会通过Unsafe的loadFence方法加入一个load内存屏障,目的是避免上图用例中步骤②和StampedLock.validate中锁状态校验运算发生重排序导致锁状态校验不准确的问题。

//java.util.concurrent.locks.StampedLock#validate
public boolean validate(long stamp) {U.loadFence();return (stamp & SBITS) == (state & SBITS);}

系统相关

这部分包含两个获取系统相关信息的方法。

//返回系统指针的大小。返回值为4(32位系统)或 8(64位系统)。
public native int addressSize();
//内存页的大小,此值为2的幂次方。
public native int pageSize();

典型应用

如下图所示的代码片段,为java.nio下的工具类Bits中计算待申请内存所需内存页数量的静态方法,其依赖于Unsafe中pageSize方法获取系统内存页大小实现后续计算逻辑。

private static int pageSize = -1;
//java.nio.Bits#pageSizestatic int pageSize() {if (pageSize == -1)pageSize = unsafe().pageSize();return pageSize;}

总结

本文对Java中的sun.misc.Unsafe的用法及应用场景进行了基本介绍,我们可以看到Unsafe提供了很多便捷、有趣的API方法。即便如此,由于Unsafe中包含大量自主操作内存的方法,如若使用不当,会对程序带来许多不可控的灾难。因此对它的使用我们需要慎之又慎。

参考资料

  • OpenJDK Unsafe source
  • Java Magic. Part 4: sun.misc.Unsafe
  • JVM crashes at libjvm.so
  • Java中神奇的双刃剑–Unsafe
  • JVM源码分析之堆外内存完全解读
  • 堆外内存 之 DirectByteBuffer 详解
  • 《深入理解Java虚拟机(第2版)》

http://www.taodudu.cc/news/show-5485070.html

相关文章:

  • Unsafe介绍及CAS原理解析
  • sun.misc.Unsafe类详解
  • 『Java安全』Unsafe类
  • Unsafe堆外内存申请、回收
  • 关于Unsafe类一些知识
  • 如何使用Unsafe类方法
  • Java JDK Unsafe
  • Unsafe初探
  • golang的unsafe包
  • JUC基石——Unsafe类
  • C# 不安全代码unsafe详解
  • Java Unsafe 类
  • golang unsafe.Sizeof浅析
  • Unsafe类源码解析
  • python工具包读取图片及格式转换
  • python获取图像路径
  • 用python读取图片名称
  • python ffmpeg剪辑视频_视频剪辑什么鬼?Python 带你高效创作短视频
  • c语言开发视频剪辑,羚珑视频编辑器开发总结
  • AE视频功能
  • fastclick.js对input的点击失效!
  • 解决 react h5移动端项目 fastclick点击多次才生效的问题
  • fastclick的介绍和使用
  • tar命令如何解压指定文件到指定目录下
  • tar命令解压时如何去除目录结构及其解压到指定目录 (--strip-components N)
  • tar解压到指定文件夹 2208281732
  • Centos下tar.gz文件解压到指定目录
  • linux tar命令解压目录结构,【tar】如何用tar命令解压时 去除目录结构 及 解压到指定目录...
  • GCC是什么
  • GCC,G++介绍

Unsafe应用详解相关推荐

  1. java unsafe 详解_Java CAS操作与Unsafe类详解

    一.复习 计算机内存模型,synchronized和volatile关键字简介 二.两者对比 sychronized和volatile都解决了内存可见性问题 不同点: (1)前者是独占锁,并且存在者上 ...

  2. Unsafe 类详解

    文章转摘自:https://www.cnblogs.com/throwable/p/9139947.html 原作者: throwable Unsafe介绍 在Oracle的Jdk8无法获取到sun. ...

  3. Java双刃剑之Unsafe类详解

    前一段时间在研究juc源码的时候,发现在很多工具类中都调用了一个Unsafe类中的方法,出于好奇就想要研究一下这个类到底有什么作用,于是先查阅了一些资料,一查不要紧,很多资料中对Unsafe的态度都是 ...

  4. java unsafe park_Java中Unsafe类详解

    http://www.cnblogs.com/mickole/articles/3757278.html Java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...

  5. C语言变量unsafe,Java中Unsafe使用详解

    Unsafe介绍 Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别.不安全操作的方法,如直接访问系统内存资源.自主管理内存资源等,这些方法在提升Java运行效率.增强Java ...

  6. sun.misc.Unsafe类详解

    一.Unsafe类的作用 可以用来在任意内存地址位置处读写数据(可见,对于普通用户来说,使用起来还是比较危险的): 支持一些CAS原子操作: 二.获取Unsafe对象 以下sun.misc.Unsaf ...

  7. Java中Unsafe类详解

    http://www.cnblogs.com/mickole/articles/3757278.html java不能直接访问操作系统底层,而是通过本地方法来访问.Unsafe类提供了硬件级别的原子操 ...

  8. Unsafe 使用详解

    Unsafe是jdk提供的一个直接访问操作系统资源的工具类(底层c++实现),它可以直接分配内存,内存复制,copy,提供cpu级别的CAS乐观锁等操作.Unsafe位于sun.misc包下,jdk中 ...

  9. Unsafe类方法详解

    jdk11.0可以直接getUnsafe()获取到,jdk8是return null: jdk11: jdk1.8: 分配内存的方法:相当于C语言中的memoryAllocation分配内存的方法.C ...

最新文章

  1. FFT ---- 2021牛客多校第一场 H Hash Function
  2. MS:中山大学丁涛/吴忠道-肠道菌群调控血吸虫病传播媒介光滑双脐螺适生性的新机制...
  3. R语言使用比例-位置图(Scale-Location Plot)和Breusch-Pagan检验验证回归模型残差的同方差性(方差齐性)
  4. 从「王师傅一共损失了多少钱」说开去
  5. JimuReport积木报表与JeecgBoot集成文档—开源免费的报表工具!
  6. 46个不可不知的生活小常识
  7. 不肯去幼儿园的小盆友
  8. IT兄弟连 JavaWeb教程 文件下载技术
  9. 不同的数据库之间导数据
  10. LAMP架构之4——MySQL源码编译及使用
  11. 微服务架构之监控预警
  12. webstack开源网站导航源码
  13. MMA7455L笔记
  14. 以太网的CSMA/CD协议
  15. PAT B 1068 万绿丛中一点红(C语言)*排除法
  16. USB PD协议规范
  17. 今日恐慌与贪婪指数为15 恐慌程度有所上升
  18. SpringMVC注解@valid与@validata,@null,@notblank,@NotEmpty
  19. Android音乐浮窗播放器
  20. 大数据舆情传播演变规律如何分析的方法技巧

热门文章

  1. 《Word中同一标点符号在不同位置空白间距不一致应怎样调》
  2. android 华为虚拟键盘不适配问题
  3. Python基础知识例题
  4. MySQL和MSSQL的区别
  5. Proteus8.9程序安装及注意事项
  6. 如果你能看到,请转发,希望它能传遍中国
  7. Java高级--JDK8新特性
  8. php 淘宝、天猫店铺商品采集
  9. JS 返回上一页判断
  10. vb.net 教程 4-7 文本文件编码 2