欢迎光临BDM
一枚菜鸟码农的成仙之路

如何计算一个 Java 对象的内存?

之所以用这个标题来写这篇文章而不是【如何使用 JMap】、【如何分析 Dump】等,是因为我一开始确实只想知道该如何知道一个 Java 的内存,毕竟优化的前提永远是可度量的量化指标

前言

许多同为新手的小伙伴们肯定遇到过,在实现一个需求的时候,脑海中浮现出两种不同的实现方式,强迫症使你很想在【时间】和【空间】复杂度上进行衡量,像 LeetCode 和 CodeWar 一样,然后选择最佳的路线。但是,无论是【时间】还是【空间】基本都只能以自己微薄的数据结构知识进行估计,稍微灵活一点的就是用System.currentTimeMillis()放在两端比较差值来计算【时间】了。

LeetCode最希望见到的字符串:您的执行用时已经战胜 99% 的Java提交记录

今天正好我在写面试作品时也遇到了这个需求,而我实在不知道也记不清哪个类占据多少,什么double比较Double该如何选型等。

恰好强迫症如我一定要搞清楚哪个好,于是翻阅搜索引擎,一共得知了两种方法。第一种 Java 代码编写的自动化估算,通过反射获取类型,再根据类型进行估算,网上有很多类似的;另一种就是本文即将展示的,直接内存读取法,直接看到对象占据了 JVM 占据了服务器多少的空间。

建议有实力的大家自己手工实现一下第一种,可以建立对 Java 对象内存结构的清晰认知,避免像我一样不知所措。

工具链

获取对象内存使用的工具主要是 JMapJVisualVm,两款都是 JDK 自带的工具,在 jdk_path/bin 目录下。

配好了环境变量后,可以直接在命令行里使用上述命令。如果使用了 Windows 安装版,环境变量会自动配置。如果命令行里能使用 Java 而不能使用上述命令,检查一下环境变量配的是 JDK 的还是 JRE 的。

基本流程

  1. 首先通过 任务管理器 或者 ps / jps 等命令找到想导出的 Java 进程的 pid。

不知道如何找请看我的另一篇基础文章 解决端口占用及快速定位Java进程

  1. 通过 jmap 命令导出该 Java 进程的 heap dump 文件。
# 仅为示例。具体使用方式建议自行查看 jmap -h
jmap -dump:live,format=b,file=./heap.bin [pid]

这样,当前文件夹(指定了./)下就会生成heap.bin 文件。它里面存放了那一时刻内存的所有信息。

  1. 直接输入命令 jvisualvm 打开内置的 Visual Vm。

Visual Vm 自带图形化界面,有诸多丰富的功能。内置的是 Java 版本发行期的稳定版本,可去官网上下载最新版,暂时还不知道有什么区别。

打开后如下图所示:

在左上角选择【文件】-【装入】后,选择 heap 文件,便会出现上图中间的信息。左边区域代表着一些操作选项及本地运行的线程。右边是打开的搜索框。

对了,我们的 dump 的目标如下。依然不忘初心,我们只想获得 Double 和 double 在内存占用上的区别。

public class TestDouble {

    public static Double thePackingType = 1d;

    public static double theBasicType = 1d;

    public static void main(String[] args) {
        while (true) {
            // Don't die
        }
    }
}

选择到【类】之后,通过下方的搜索,输入 Double 可检索获得 java.lang.Double 的内存类别。然后双击点进去可以看到单个 Double 实例为 24 字节,如下图所示:

double 这种基础类型在 visualvm 里没找到,于是通过查询资料(记忆)获取到它是 8 字节

其实通过 Double 类的源码可以看到,Double 对象里都包含一个 double 类型的 Value,多出来的 16 字节是用于对象头的开销。对象头在 64 位系统下是 16 字节,32 位下是 8 字节。

三倍的内存差别,还是很明显。在 Double[] 和 double[] 选型上,能不用 Double[] 就不用。


参考文档:

本文遵守知识共享署名-相同方式共享 4.0 国际许可协议,未经允许不得转载暂时没有标题 » 如何计算一个 Java 对象的内存?

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

联系我们GitHub