java 对象实际占用内存大小预估工具类ObjectSizeUtil

java 对象实际占用内存大小预估工具类

返回对象占用预估字节数

返回字节数对应格式化后的字符串(xx Kb)

使用效果
在这里插入图片描述
在这里插入图片描述
依赖了lombock和hutool,项目不用这个可以去掉日志,稍微改写一下。


import cn.hutool.core.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author humorchen
 * date: 2024/4/1
 * description: 对象大小计算工具
 **/
@Slf4j
public class ObjectSizeUtil {
    /**
     * 嵌套深度不超过10层
     */
    public static int MAX_DEPTH = 10;
    /**
     * 单位
     */
    public static final String[] UNIT_NAMES = new String[]{"B", "KB", "MB", "GB", "TB", "PB", "EB"};
    /**
     * 字段缓存
     */
    private static final Map<Class<?>, Field[]> FIELD_CACHE = new ConcurrentHashMap<>();

    /**
     * 获取对象大小,接近实际内存大小,不完全等于实际大小,用于快速计算内存大小
     * 性能测试:三层带List的对象,百万次调用,耗时 196ms
     *
     * @param o
     * @return
     */
    public static long estimateObjectSize(Object o) {
        return estimateObjectSize(o, 0);
    }

    /**
     * 获取对象大小,接近实际内存大小,不完全等于实际大小,用于快速计算内存大小
     * 性能测试:三层带List的对象,百万次调用,耗时 196ms
     *
     * @param o
     * @param depth
     * @return
     */
    private static long estimateObjectSize(Object o, int depth) {
        if (o == null) {
            return 4L;
        }
        // 深度兜底
        if (depth >= MAX_DEPTH) {
            return 4L;
        }
        // 基本类型、基本类型包装类、枚举、数组、集合、Map、注解、其他类
        Class<?> cls = o.getClass();
        // 64位机器上每个对象至少有16字节的开销
        long size = 16L;
        // 基本类和包装对象 int 的class是java.lang.Integer
        if (ClassUtil.isBasicType(cls)) {
            if (cls == Integer.class || cls == Float.class) {
                size += 4L;
            } else if (cls == Long.class || cls == Double.class) {
                size += 8L;
            } else if (cls == Short.class || cls == Byte.class) {
                size += 2L;
            } else if (cls == Character.class || cls == Boolean.class) {
                size += 1L;
            }
        } else if (cls == String.class) {
            // 字符串,每个字符2字节
            size += 2L * ((String) o).length();
        } else if (cls.isEnum()) {
            // 枚举为常量,内存占用视为一个对象引用地址4字节
            size = 4L;
        } else if (cls.isAnnotation()) {
            // 注解 内存占用视为一个对象引用地址4字节
            size = 4L;
        } else if (cls.isArray()) {
            // 数组 遍历数组中的每个元素,递归调用estimateObjectSize方法,数组里每个对象指针4字节
            int len = Array.getLength(o);
            for (int i = 0; i < len; i++) {
                size += estimateObjectSize(Array.get(o, i), depth + 1) + 4;
            }
        } else if (o instanceof Collection) {
            // 集合 遍历集合中的每个元素,递归调用estimateObjectSize方法
            Collection<?> collection = (Collection<?>) o;
            if (!collection.isEmpty()) {
                for (Object obj : collection) {
                    // 集合里每个对象指针4字节
                    size += estimateObjectSize(obj, depth + 1) + 4;
                }
            }
        } else if (o instanceof Map) {
            // Map 遍历Map中的每个元素,递归调用estimateObjectSize方法
            Map<?, ?> map = (Map<?, ?>) o;
            if (!map.isEmpty()) {
                for (Map.Entry<?, ?> entry : map.entrySet()) {
                    // 集合里每个对象指针4字节 *2
                    size += 8;
                    size += estimateObjectSize(entry.getKey(), depth + 1);
                    size += estimateObjectSize(entry.getValue(), depth + 1);
                }
            }
        } else {
            // 其他类,遍历类中的每个字段,递归调用estimateObjectSize方法,计算字段的内存占用,field获取每次执行都会拷贝,所以用缓存
            Field[] declaredFields = FIELD_CACHE.computeIfAbsent(cls, k -> {
                // 获取类声明的字段,会拷贝Field[]
                Field[] fields = getDeclaredFields(cls);
                // 设置可访问
                for (Field field : fields) {
                    field.setAccessible(true);
                }
                return fields;
            });
            for (Field declaredField : declaredFields) {
                try {
                    // 静态的字段直接算一个指针的内存
                    if (Modifier.isStatic(declaredField.getModifiers())) {
                        size += 4L;
                    } else {
                        // 当前字段的这个指针的内存占用加指向的对象内存占用
                        Object object = declaredField.get(o);
                        size += estimateObjectSize(object, depth + 1) + 4;
                    }
                } catch (Exception e) {
                    log.error("获取对象大小异常", e);
                }
            }
        }
        return size;
    }

    /**
     * 获取类声明的字段
     *
     * @param cls
     * @return
     */
    private static Field[] getDeclaredFields(Class<?> cls) {
        Set<Field> set = new LinkedHashSet<>();
        Class<?> currentClass = cls;
        int depth = 0;
        while (!Object.class.equals(currentClass) && depth < MAX_DEPTH) {
            set.addAll(Arrays.asList(currentClass.getDeclaredFields()));
            currentClass = currentClass.getSuperclass();
            depth++;
        }
        return set.toArray(new Field[0]);
    }

    /**
     * 格式化字节数
     *
     * @param size
     * @return
     */
    public static String formatSize(long size) {
        if (size <= 0) {
            return "0";
        }
        int digitGroups = Math.min(UNIT_NAMES.length - 1, (int) (Math.log10(size) / Math.log10(1024)));
        return new DecimalFormat("#,##0.##")
                .format(size / Math.pow(1024, digitGroups)) + " " + UNIT_NAMES[digitGroups];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/719340.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java同城信息付费系统家政服务房屋租赁房屋买卖房屋装修信息发布平台小程序APP公众号源码

&#x1f4f2;一站式便捷生活助手&#x1f680; &#x1f4cc;一、引言&#xff1a;同城信息付费服务系统的新篇章 在信息碎片化的时代&#xff0c;我们越来越需要一个集中、高效、便捷的平台来获取同城信息。同城信息付费服务系统发布平台小程序应运而生&#xff0c;它以其独…

CorelDRAW Graphics Suite下载2024最新版-CorelDRAW2024详细安装步骤

CorelDRAW​​ Graphics Suite官方版是款很多用户在工作中都会使用的矢量图形设计工具。CorelDRAW Graphics Suite正式版采用量身定制的界面和无与伦比的定制功能&#xff0c;畅享无缝设计经验。并且CorelDRAW Graphics Suite还可以广泛应用于商标设计、标志制作、模型绘制、插…

物理隔离后数据怎么导入和导出?安全U盘一键解决

政府单位、军工和科研所、航空航天企业、金融机构、医疗单位、电力企业、生物制药实验室等企业及单位&#xff0c;因研发和生产过程、或日常经营中涉及大量敏感信息和技术&#xff0c;需要通过物理隔离来确保网络的安全性。因此&#xff0c;多采用物理隔离的方式进行网络建设。…

Python xml.dom.minidom 读取XML元素

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 什么是 XML&#xff1f; XML&#xff08;可扩展标记语言&#xff09;是一种用于描述数据结构和交换数据的标记语言。它被广泛用于 Web 应用程序中&#xff0c;用于存储和传输数据。XML 具有自描述性&#xff0c;因此…

002.Linux CentOS7 安装

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

Flink 窗口函数

一、Window 概述 Flink 流式计算是一种被设计用于处理无限数据集的数据处理引擎&#xff0c;而无限数据集是指一种不断增长的本质上无限的数据集&#xff0c;而 window 是一种切割无线数据为有限块进行处理的手段。 二、Window 分类 Window 可以分为两类&#xff1a; Count…

Python | Leetcode Python题解之第162题寻找峰值

题目&#xff1a; 题解&#xff1a; class Solution:def findPeakElement(self, nums: List[int]) -> int:n len(nums)# 辅助函数&#xff0c;输入下标 i&#xff0c;返回 nums[i] 的值# 方便处理 nums[-1] 以及 nums[n] 的边界情况def get(i: int) -> int:if i -1 or…

售前方案VS产品经理

售前和产品经理分别是做什么的 售前方案&#xff1a;主要负责在销售过程中与客户沟通&#xff0c;了解客户需求&#xff0c;提供技术支持和解决方案演示的技术人员。协助销售团队与客户沟通&#xff0c;帮助客户理解产品特性和技术细节&#xff0c;确保产品与客户需求匹配。售前…

刷代码随想录有感(107):动态规划——01背包/最后一块石头的重量||

题干&#xff1a; 代码&#xff1a; class Solution { public:int lastStoneWeightII(vector<int>& stones) {vector<int>dp(15001, 0);int sum 0;for(int i : stones){sum i;}int target sum / 2;for(int i 0; i < stones.size(); i){for(int j targ…

为什么选择住宅代理?IPXProxy住宅代理的优势

​在数字化时代&#xff0c;隐私和数据安全成为了每个互联网用户的关注焦点。住宅代理作为一种有效保护隐私的工具&#xff0c;因其独特的优势备受推崇。本文将从用户的角度&#xff0c;探讨选择住宅代理的诸多优势。 什么是住宅代理&#xff1f; 简单来说&#xff0c;住宅代理…

中华活页文选杂志中华活页文选杂志社中华活页文选编辑部2024年第8期目录

语文教学_阅读与鉴赏 小学语文拓展性阅读教学策略探究 魏萌萌; 4-6 小学语文阅读教学中培养学生创新思维的策略分析 谈晓娟; 7-9 新课改背景下小学语文阅读教学策略研究——以《丑小鸭》一课为例 许巧林; 10-12《中华活页文选》投稿&#xff1a;cn7kantougao163.co…

typora下载安装、激活教程

目录 介绍 基本功能 使用教程 高级功能 下载安装 激活 关闭软件每次启动时的已激活弹窗 去除软件左下角“未激活”提示 介绍 Typora 是一款功能强大的 Markdown 编辑器&#xff0c;它以其简洁的界面设计和高效的文本编辑能力受到许多用户的青睐。Typora 的主要特点在于…

Geek新鲜事15: Linus Torvalds 发话了,新调度器sched_ext将合入Linux6.11

“ext”对应的英文单词为“extensible”&#xff0c;意为可扩展的。开发者Tejun Heo通过整整30 个的patchs&#xff0c;提供了一个支持eBPF程序修改调度策略的调度类。其核心目的有三个&#xff1a; 让开发者更易于实验和探索新的调度策略&#xff0c;免去编译完整内核镜像的成…

超长国债来了,高净值客群的机会在哪儿?

有人说&#xff0c;2024年是全球经济的“分化年”&#xff0c;也是中国经济突围的“关键年”。当前&#xff0c;我国经济恢复仍处在关键阶段&#xff0c;长期向好的基本趋势没有改变&#xff0c;但也需要克服一些挑战&#xff0c;而巩固和增强经济复苏的良好势头&#xff0c;离…

楼顶气膜体育馆建设的关键问题解析—轻空间

随着城市化进程的加快和土地资源的日益紧张&#xff0c;楼顶气膜体育馆作为一种新兴的建筑形式备受关注。其轻盈美观、节省用地、施工便捷等特点&#xff0c;使其成为城市空间利用的理想选择。那么&#xff0c;在楼顶建设气膜体育馆有哪些关键问题需要考虑呢&#xff1f; 一、楼…

Idea Git中 unversioned files的处理

项目中&#xff0c;使用git commit命令可以查看当前所在的分支&#xff0c;以及当前改动的文件&#xff0c;可以使用快捷键Alt 0打开/关闭&#xff1b;如下图所示&#xff0c; 可以看到分成了两个不同的区域&#xff0c; Changes 表示有改动的文件&#xff0c;包括修改、新增…

【Linux】基础IO——文件描述符,重定向,FILE

话接上篇&#xff1a; 1.文件描述符fd 磁盘文件 VS 内存文件&#xff1f; 当文件存储在磁盘当中时&#xff0c;我们将其称之为磁盘文件&#xff0c;而当磁盘文件被加载到内存当中后&#xff0c;我们将加载到内存当中的文件称之为内存文件。磁盘文件和内存文件之间的关系就像程…

【机器学习】智能创意工厂:机器学习驱动的AIGC,打造未来内容新生态

&#x1f680;时空传送门 &#x1f50d;机器学习在AIGC中的核心技术&#x1f4d5;深度学习&#x1f388;生成对抗网络&#xff08;GANs&#xff09; &#x1f680;机器学习在AIGC中的具体应用&#x1f340;图像生成与编辑⭐文本生成与对话系统&#x1f320;音频生成与语音合成 …

汇凯金业:现货黄金交易的分析方法投资策略

现货黄金交易中&#xff0c;分析方法主要分为两大类&#xff1a;技术面分析和基本面分析。两者各有特点和应用场景&#xff0c;投资者需要结合自身的交易风格和市场情况进行综合运用。本文将详细介绍这两种分析方法&#xff0c;帮助投资者更好地进行现货黄金交易。 一、基本面…

基于java《场馆预约MeetHere》【完整代码】和【完整测试流程报告】的资源

基于java《场馆预约MeetHere》【完整代码】和【完整测试流程报告】的资源 项目描述 MeetHere是一个场馆预约和管理的Web商务网站 普通用户&#xff1a;注册、登录、个人信息管理、查看场馆介绍和预约信息、场馆预约、场馆预约订单管理、查看新闻、留言管理&#xff08;发布、浏…