Unsafe-Java永远的“神”(一)

简介: Unsafe-Java永远的“神”(一)

简要介绍:

Java语言先比较与C和C++有一个非常大的不同点在于Java语言无法直接操作内存,实际开发中,默认都是由JVM来进行内存分配和垃圾回收,而JVM在进行垃圾回收的时候,绝大多数垃圾回收器都需要STW(stop the world)这个问题往往会导致服务短暂或者较长时间的暂停。因此Unsafe提供了通过Java直接操作内存的API,尽管Unsafe是JavaNIO和并发的核心类,但是其如其名,这是一个官方不推荐开发者使用的及其不安全的类!


主要作用:

image.png

一、获取Unsafe

源码-基于jdk1.8image.png

二、操作方法

数组操作image.pngimage.png对象操作

package com.liziba.unsafe;
import com.liziba.unsafe.pojo.User;
import sun.misc.Unsafe;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
 * <p>
 *      操作对象示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/5/24 20:40
 */
public class OperateObjectExample {
    /**
     *  1、public native Object allocateInstance(Class<?> var1); 分配内存
     *  2、public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); 方法定义一个类用于动态的创建类
     * @throws Exception
     */
    public static void operateObjectUseUnsafe() throws Exception{
        Unsafe unsafe = UnsafeFactory.getUnsafe();
        // 使用Unsafe的allocateInstance()方法,可以无需使用构造函数的情况下实例化对象
        User user = (User) unsafe.allocateInstance(User.class);
        user.setId(1);
        user.setName("李子捌");
        System.out.println(user);
        // 返回对象成员属性在内存中相对于对象在内存中地址的偏移量
        Field name = User.class.getDeclaredField("name");
        long fieldOffset = unsafe.objectFieldOffset(name);
        // 使用Unsafe的putXxx()方法,可以直接修改内存地址指向的数据(可以越过权限访问控制符)
        unsafe.putObject(user, fieldOffset, "李子柒");
        System.out.println(user);
        // 使用Unsafe在运行时通过.class文件,创建类
        File classFile = new File("E:\\workspaceall\\liziba-javap5\\out\\production\\liziba-javap5\\com\\liziba\\unsafe\\pojo\\User.class");
        FileInputStream fis = new FileInputStream(classFile);
        byte [] classContent = new byte[(int) classFile.length()];
        fis.read(classContent);
        Class<?> clazz = unsafe.defineClass(null, classContent, 0, classContent.length, null, null);
        Constructor<?> constructor = clazz.getDeclaredConstructor(int.class, String.class);
        System.out.println(constructor.newInstance(1, "李子玖"));
    }
    public static void main(String[] args) {
        try {
            OperateObjectExample.operateObjectUseUnsafe();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果image.png

内存操作

package com.liziba.unsafe;
import sun.misc.Unsafe;
/**
 * <p>
 *      內存地址操作示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/5/24 21:32
 */
public class OperateMemoryExample {
    /**
     * 1、public native long allocateMemory(long var1);  分配var1字节大小的内存,返回起始地址偏移量
     * 2、public native long reallocateMemory(long var1, long var3);  重新给var1起始地址的内存分配长度为var3字节的内存,返回新的内存起始地址偏移量
     * 3、public native void freeMemory(long var1); 释放起始地址为var1的地址
     *
     * 分配地址的方法还有重分配,都是分配在堆外内存,返回的是一个long类型的地址偏移量。这个偏移量在Java程序中的每一块内存都是唯一的
     *
     */
    public static void operateMemoryUseUnsafe() {
        Unsafe unsafe = UnsafeFactory.getUnsafe();
        // 申请分配8byte的内存
        long address = unsafe.allocateMemory(1L);
        // 初始化内存填充值
        unsafe.putByte(address, (byte)1);
        // 测试输出
        System.out.println(new StringBuilder().append("address: ").append(address).append(" byte value: ").append(unsafe.getByte(address)));
        // 重新分配一个地址
        long newAddress = unsafe.reallocateMemory(address, 8L);
        unsafe.putLong(newAddress, 8888L);
        System.out.println(new StringBuilder().append("address: ").append(newAddress).append(" long value: ").append(unsafe.getLong(newAddress)));
        // 释放地址,注意地址可能被其他使用
        unsafe.freeMemory(newAddress);
        System.out.println(new StringBuilder().append("address: ").append(newAddress).append(" long value: ").append(unsafe.getLong(newAddress)));
    }
    public static void main(String[] args) {
        OperateMemoryExample.operateMemoryUseUnsafe();
    }
}

image.png

CAS操作

package com.liziba.unsafe;
import com.liziba.unsafe.pojo.User;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
 * <p>
 *      CAS操作示例
 * </p>
 *
 * @Author: Liziba
 * @Date: 2021/5/24 22:18
 */
public class OperateCASExample {
    /**
     * CAS == compare and swap(比较并替换)
     * 当需要改变的值为期望值的时候,就替换为新的值,是原子(不可再分割)操作。Java中大量的并发框架底层使用到了CAS操作。
     * 优势:无锁操作,减少线程切换带来的开销
     * 缺点:CAS容易在并发的情况下失败从而引发性能问题,也存在ABA问题。
     *
     * Unsafe中提供了三个方法
     * 1、compareAndSwapInt
     * 2、compareAndSwapLong
     * 3、compareAndSwapObject
     *
     */
    public static void operateCASUseUnsafe() throws Exception {
        User user = new User(1, "李子捌");
        System.out.println("pre user value: " + user);
        Unsafe unsafe = UnsafeFactory.getUnsafe();
        Field id = user.getClass().getDeclaredField("id");
        Field name = user.getClass().getDeclaredField("name");
        // 获取ID字段的内存偏移量
        long idFieldOffset = unsafe.objectFieldOffset(id);
        // 获取name字段的内存偏移量
        long nameFieldOffset = unsafe.objectFieldOffset(name);
        // 如果ID的期望值是1,则修改为18  success
        unsafe.compareAndSwapInt(user, idFieldOffset, 1, 18);
        // 如果name的期望值是小荔枝,则修改为李子柒  fail
        unsafe.compareAndSwapObject(user, nameFieldOffset, "小荔枝", "李子柒");
        // 输出修改的user对象
        System.out.println("post user value: " + user);
    }
    public static void main(String[] args) {
        try {
            OperateCASExample.operateCASUseUnsafe();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

image.png

/**
 * 查看Java的java.util.concurrent.locks.LockSupport源代码可以发现LockSupport类
 * 中有各种版本的pack方法但是最终都是通过调用Unsafe.park()方法实现的。
 */
public class LockSupport {
  public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }
    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }
    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }
    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }
     public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }
}
目录
相关文章
|
6月前
|
安全 Java 程序员
[笔记] 疯狂JAVA讲义(第3版)第7章 Java基础类库
[笔记] 疯狂JAVA讲义(第3版)第7章 Java基础类库
|
6月前
|
Java 编译器
17. 【Java教程】Java 方法
17. 【Java教程】Java 方法
41 0
|
分布式计算 安全 Java
【面试题精讲】Java中Unsafe
【面试题精讲】Java中Unsafe
java202303java学习笔记第三十四天file得成员方法1
java202303java学习笔记第三十四天file得成员方法1
43 0
java202303java学习笔记第三十四天file得成员方法2
java202303java学习笔记第三十四天file得成员方法2
47 0
java202303java学习笔记第四十六天javaweb-tomacat介绍2
java202303java学习笔记第四十六天javaweb-tomacat介绍2
52 0
java202303java学习笔记第四十六天javaweb-tomacat介绍1
java202303java学习笔记第四十六天javaweb-tomacat介绍1
38 0
|
Java 程序员 开发工具
【Hello Java】java学习记录(1)
【Hello Java】java学习记录(1)
【Hello Java】java学习记录(1)
|
存储 安全 Java
Java基于JDK 1.8的LinkedList源码详析
Java基于JDK 1.8的LinkedList源码详析
Java基于JDK 1.8的LinkedList源码详析
|
XML IDE 前端开发
Java 程序员都需要懂的 反射!
今天来简单写一下Java的反射。本来没打算写反射这个知识点的,只是不少的读者都问过我:“你的知识点好像缺了反射阿。能不能补一下?”
132 0
Java 程序员都需要懂的 反射!
下一篇
DataWorks