Unsafe使用示例代码(二)

简介: Unsafe使用示例代码(二)

我们平时如何实现浅克隆?

  • 实现Closeable接口
  • 重写close()方法

一、Unsafe实现浅克隆

浅克隆工具类

package com.liziba.unsafe.clone;


import com.liziba.unsafe.UnsafeFactory;

import sun.misc.Unsafe;


import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

import java.util.Arrays;


/**

* <p>

*      浅克隆工具类

* </p>

*

* @Author: Liziba

* @Date: 2021/5/26 21:08

*/

public class ShallowCloneUtil {



   /**

    * 获取对象的内存地址

    *

    * @Description

    * Unsafe类没有提供直接获取实例对象内存地址的方法,但是可以通过以下方式间接获取。

    * 构建对象A,A包含了我们需要获取内存地址的B对象的引用,这样只有获取到A对象持有的B对象的引用地址,就可以知道B对象的地址了。

    * 我们可以通过Unsafe类获取内存地址的方法public native long getLong(Object var1, long var2)来获取;

    * 此处我们为了方便,通过数组Object[] 添加Object元素,持有Object的引用

    *

    * @return

    */

   public static Long getAddress(Object obj) {


       Object[] objects = new Object[] {obj};

       Unsafe unsafe = UnsafeFactory.getUnsafe();

       int arrayBaseOffset = unsafe.arrayBaseOffset(Object[].class);

       return unsafe.getLong(objects, arrayBaseOffset);

   }



   /**

    * 获取对象的大小

    *

    * @Dscription

    * Java中实例化一个对象时,JVM会在堆中分配非static的Field的内存,其他的static属性或者method在类加载期间或者JVM启动时已经存放在内存中。

    * 所以我们计算对象的大小的时候只需要求和Field的大小就行了,JVM分配内存时,单个实例对象中的Field内存是连续不断地,

    * 因此我们只需获取最大偏移量Filed的偏移量 + 最大偏移量Filed本身的大小即可

    *

    * Java中基本数据类型所占的字节数

    * byte/boolean     1 字节

    * char/short       2 字节

    * int/float        4 字节

    * long/double      8 字节

    * boolean 理论上占1/8字节,实际上按照1byte处理。

    * Java采用的是Unicode编码,每一个字节占8位,一个字节由8个二进制位组成。

    *

    * @param clazz

    * @return

    */

   public static Long size(Class clazz) {


       // 最后一个Filed的内存偏移量

       long maxOffset = 0;

       Class lastFiledClass = null;

       Unsafe unsafe = UnsafeFactory.getUnsafe();

       do {

           for (Field field : clazz.getDeclaredFields()) {

               if (!Modifier.isStatic(field.getModifiers())) {

                   long tmpOffset = unsafe.objectFieldOffset(field);

                   if (tmpOffset > maxOffset) {

                       maxOffset = tmpOffset;

                       lastFiledClass = field.getType();

                   }

               }

           }

       } while ((clazz = clazz.getSuperclass()) != null);

       // 最后一个Field本身的大小

       int lastFiledSize = (boolean.class.equals(lastFiledClass) || byte.class.equals(lastFiledClass)) ? 1 :

                               (short.class.equals(lastFiledClass) || char.class.equals(lastFiledClass)) ? 2 :

                                    (int.class.equals(lastFiledClass) || float.class.equals(lastFiledClass)) ? 4 :  8 ;


       return maxOffset + lastFiledSize;

   }



   /**

    * 申请一块固定大小的内存空间

    *

    * @Description

    * 通过Unsafe的public native long allocateMemory(long var1);申请一块内存空间。

    *

    * @param bytes  需要申请的内存大小

    * @return

    */

   public static Long allocateMemory(long bytes) {

       return UnsafeFactory.getUnsafe().allocateMemory(bytes);

   }



   /**

    * 从原对象内存地址srcAddr复制大小位size的内存到destAddr地址处

    *

    * @param srcAddr           源地址

    * @param destAddr          目标地址

    * @param size              复制内存大小

    */

   public static void copyMemory(long srcAddr, long destAddr, long size) {

       UnsafeFactory.getUnsafe().copyMemory(srcAddr, destAddr, size);

   }



   /**

    * Unsafe未提供直接读取内存转为Java对象的方法,但是可以通过新建一个包含T类型属性的对象将申请的内存地址赋值给T属性

    *

    * @param addr

    * @param <T>

    * @return

    */

   public static <T> T addressConvertObject(long addr) {


       Object[] objects = new Object[] {null};

       Unsafe unsafe = UnsafeFactory.getUnsafe();

       int baseOffset = unsafe.arrayBaseOffset(Object.class);

       unsafe.putLong(objects, baseOffset, addr);

       return (T)objects[0];

   }



   /**

    * 实现对象的浅克隆

    *

    * @Description

    * 数组的无法通过此方法实现克隆,数组类是在JVM运行时动态生成的

    *

    * @param t

    * @param <T>

    * @return

    */

   public static <T> T shallowClone(T t) {

       Class<?> clazz = t.getClass();

       if (clazz.isArray()) {

           Object[] objects = (Object[]) t;

           return (T) Arrays.copyOf(objects, objects.length);

       }

       Long srcAddr = getAddress(t);

       Long size = size(clazz);

       Long destAddr = allocateMemory(size);

       copyMemory(srcAddr, destAddr, size);

       return addressConvertObject(destAddr);

   }



}


测试

/**

* <p>

*      浅克隆测试

* </p>

*

* @Author: Liziba

* @Date: 2021/5/24 23:11

*/

public class ShallowCloneTest {


   public static void main(String[] args){


       SimpleInfo simpleInfo = new SimpleInfo();

       simpleInfo.setId(1);

       System.out.println(simpleInfo.hashCode());

       SimpleInfo clone = ShallowCloneUtil.shallowClone(simpleInfo);

       System.out.println(clone.hashCode());

   }


}

更完善的方法

https://blog.csdn.net/zhxdick/article/details/52003123?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242

目录
相关文章
|
1天前
|
安全 Java
unsafe类和varhandle类讲解
本文介绍了Java中的Unsafe类和VarHandle类,展示了Unsafe类如何通过底层操作绕过Java的安全限制直接访问内存和对象,以及VarHandle类如何在Java 9及以上版本中提供原子性和可变性访问。
6 1
unsafe类和varhandle类讲解
|
11月前
|
存储 Java 编译器
Java 程序里 transient 关键字的使用方法介绍
Java 程序里 transient 关键字的使用方法介绍
|
12月前
|
安全 Java 编译器
【Java用法】Java关键字 —— final的用法
【Java用法】Java关键字 —— final的用法
91 0
|
存储 安全 编译器
Go语言源码剖析-String和unsafe包
Go语言源码剖析-String和unsafe包
68 0
|
存储 缓存 安全
Java Volatile Atomic关键字
Java Volatile Atomic关键字
46 0
|
Oracle 前端开发 算法
JDK源码(21)-Unsafe
JDK源码(21)-Unsafe
167 0
JDK源码(21)-Unsafe
|
Java
一文带你了解Java中的static 静态关键字和final 不可变关键字
本文比较偏向基础,适合初学者观看学习参考。本文主要介绍了static 静态关键字、静态static关键字修饰成员变量、静态static关键字修饰成员方法、静态代码块、 final 关键字、final关键字用于修饰类、final关键字用于修饰成员方法 、final关键字用于修饰局部变量、final关键字用于修饰成员变量。
141 0
一文带你了解Java中的static 静态关键字和final 不可变关键字
|
Java 程序员 API
JVM系列(五):native关键字用法介绍
native关键字修饰的Java方法是一个原生态方法,方法对应的实现Java作用范围达不到,而是在用其他编程语言(如C和C++)文件中实现。Java语言本身不能直接对操作系统底层进行访问和操作,但可以通过JNI接口调用其他编程语言来实现对操作系统底层的访问。
JVM系列(五):native关键字用法介绍
|
缓存 安全 Java
Java 中 volatile 用法
在 Java 并发编程中,volatile 是经常用到的一个关键字,它可以用于保证不同的线程共享一个变量时每次都能获取最新的值。volatile 具有锁的部分功能并且性能比锁更好,所以也被称为轻量级锁。下面具体分析 volatile 的用法及原理,涉及到内存模型、可见性、重排序以及伪共享等方面。
Java:原子操作类AtomicInteger代码示例
Java:原子操作类AtomicInteger代码示例