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

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

订阅专栏

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


实现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


目录
相关文章
|
Java 测试技术 Spring
springboot测试环境无法注入bean问题分析及解决方案
springboot测试环境无法注入bean问题分析及解决方案
4592 0
springboot测试环境无法注入bean问题分析及解决方案
|
JSON Java 数据格式
|
12月前
|
存储 Java
Bitmap位图(Java实现)
本文介绍了使用Java实现一个简单的Bitmap,通过自定义byte数组存储数据,提供put和exist方法分别用于插入数据和查询数据是否存在。Bitmap利用位操作高效地管理大量布尔值,适用于空间优化的场景。代码中详细解释了位图的核心原理、方法实现及边界检查。后续计划探讨位图在海量数据去重中的应用及JDK BitSet源码分析。
764 7
|
8月前
|
前端开发 测试技术 API
2025年API开发必备:10款优秀Postman替代工具大盘点
API测试在现代开发中至关重要,Postman虽为首选,但市场上涌现出许多优秀替代工具。本文精选2025年10款好评如潮的API测试工具:Apifox、Insomnia、Hoppscotch、Paw、Talend API Tester、HTTPie、ARC、Swagger UI、SoapUI和Thunder Client。这些工具各具特色,满足不同需求,如团队协作、开源易用、自动化测试等。无论是简洁轻量还是功能全面,总有一款适合你的团队,助力效率提升。
3818 121
|
10月前
|
存储 前端开发 数据可视化
Grafana Loki,轻量级日志系统
本文介绍了基于Grafana、Loki和Alloy构建的轻量级日志系统。Loki是一个由Grafana Labs开发的日志聚合系统,具备高可用性和多租户支持,专注于日志而非指标,通过标签索引而非内容索引实现高效存储。Alloy则是用于收集和转发日志至Loki的强大工具。文章详细描述了系统的架构、组件及其工作流程,并提供了快速搭建指南,包括准备步骤、部署命令及验证方法。此外,还展示了如何使用Grafana查看日志,以及一些基本的LogQL查询示例。最后,作者探讨了Loki架构的独特之处,提出了“巨型单体模块化”的概念,即一个应用既可单体部署也可分布式部署,整体协同实现全部功能。
3473 70
Grafana Loki,轻量级日志系统
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
9123 119
|
12月前
|
数据安全/隐私保护
思科模拟器Cisco Packet Tracer 8.2.1注册、下载和安装教程(正确+详细)
本文详细总结了思科模拟器Cisco Packet Tracer 8.2.1注册、下载和安装教程(正确+详细),看这一篇就够啦~
38735 6
思科模拟器Cisco Packet Tracer 8.2.1注册、下载和安装教程(正确+详细)
|
关系型数据库 MySQL 数据库
上手体验 PolarDB-X 数据库
PolarDB-X,一款高性能云原生分布式数据库。
687 2

热门文章

最新文章