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

目录
相关文章
|
3月前
|
安全 Java
unsafe类和varhandle类讲解
本文介绍了Java中的Unsafe类和VarHandle类,展示了Unsafe类如何通过底层操作绕过Java的安全限制直接访问内存和对象,以及VarHandle类如何在Java 9及以上版本中提供原子性和可变性访问。
45 1
unsafe类和varhandle类讲解
|
5月前
|
存储 Java 编译器
深入理解 go unsafe
深入理解 go unsafe
30 0
|
5月前
|
Java
【Java】static 类方法中注意事项
【Java】static 类方法中注意事项
|
8月前
|
存储 Java
Java之Stream流及方法引用的详细解析二
2.6Stream流综合练习【应用】 案例需求 现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作 男演员只要名字为3个字的前三人 女演员只要姓林的,并且不要第一个
86 0
|
Java Python
java调用python脚本并传递参数list
java调用python脚本并传递参数list
132 0
|
存储 安全 编译器
Go语言源码剖析-String和unsafe包
Go语言源码剖析-String和unsafe包
80 0
|
Java
Java中super函数的用法
Java中super函数的用法
137 0
|
Dart
Dart之 对象call方法
Dart之 对象call方法
225 0
Dart之 对象call方法
Java类Demo中存在方法func1、func2、func3和func4,请问该方法中,哪些是不合法的定义?( )
Java类Demo中存在方法func1、func2、func3和func4,请问该方法中,哪些是不合法的定义?( )
Java List的remove()方法陷阱
Java List的remove()方法陷阱
143 0