内存占用估算方法

优质博文:IT-BLOG-CN

通过掌握每种数据类型的大小,就可以更准确地预测对象和数据的内存消耗。

一、基础数据类型

Java基础数据类型结构,在64位系统开启指针压缩情况下的内存占用字节数:

boolean byte char short int long float double 引用
1 1 2 2 4 8 4 8 4

这些基础数据类型是轻量级的,应尽可能多地使用他们以减少内存消耗。

二、对象内存占用情况

Java对象的内存占用包括标记字mark word、类指针klass pointer、数据字段(实例变量)以及填充padding

markword klass pointer data padding
8 4 所有字段占用内存之和 将内存占用补齐至8的整数倍

由此可见,一个没有任何数据字段的空对象,也会占用16字节的内存空间

三、数组内存占用

数组在Java中是一种特殊类型的对象,它还包括一个额外的数组长度字段。数组的内存占用包括标记字、类指针、数组长度字段、数组元素以及填充。与对象类型,数组的占用长度也必须是8的倍数。需要注意的是,数组元素的大小取决于元素的类型和数量。

markword klass pointer 数组长度字段 数组元素 padding
8 4 4 所有字段占用内存之和 将内存占用补齐至8的整数倍

四、String对象内存占用

这里,我们以Java8为例说说空String的内存占用情况,我们来看看String类中的成员变量。

java 复制代码
/** The value is used for character storage. */
private final char value[];
 
/** Cache the hash code for the string */
private int hash; // Default to 0
 
/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;

根据上面我们对对象和数组内存的分析以及String的成员属性可以得出一个空String对象所占用的内存空间,如下所示。

java 复制代码
对象头(8 字节)+ 引用 (4 字节 )  + char 数组(16 字节)+ 1个 int(4字节)+ 1个long(8字节)= 40 字节

如果String字符串的长度大于0的话,我们也可以得出String占用内存的计算公式,如下所示。

java 复制代码
40 + 2 * n

五、内存占用分析工具

位了更准确地估计内存占用情况,我们可以借助一些内存分析工具。
jol-core是一个java官方推出的jar包,地址为GitHub-openjdk/jol

pom依赖:

xml 复制代码
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.14</version>
</dependency>

JOL中常用的几个方法:

java 复制代码
ClassLayout.parseInstance(obj).toPrintable():查看对象内部的内存布局;
ClassLayout.parseInstance(obj).instanceSize():计算对象的大小,单位为字节;

GraphLayout.parseInstance(obj).toPrintable():查看对象外部信息,包括引用的对象;
GraphLayout.parseInstance(obj).totalSize():查看对象占用空间总大小;

注:除了parseInstance(Object obj)外,还有parseClass(Class<?> class),用法基本相同。

代码使用场景:

java 复制代码
public class JolTest {
    public static void main(String[] args) {
        Object obj = new Test();
        // 查看对象内部信息
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());
    }
}
class Test{
    private long p;
    private byte p1;
    private short p2;
}

六、防止内存膨胀的策略

【1】使用基础类型代替String 在处理业务时,短字符串(如城市三字码)频繁出现。标准String对象在Java中占用至少50字节内存,这对于仅包含2-3个字符的字符串来说是一种浪费。我们可以使用CodeUtil工具将这些字符串编码为原生数据类型,从而显著降低内存占用并可能提升性能。

将字符串编码为浮点数和解码为字符串

java 复制代码
public class Main {
    public static void main(String[] args) {
        String doubleString = "123.45";
        
        // 将字符串编码为浮点数
        double encodedDouble = CodeUtil.encodeToDouble(doubleString);
        System.out.println("Encoded Double: " + encodedDouble);
        
        // 将浮点数解码为字符串
        String decodedDoubleString = CodeUtil.decodeFromDouble(encodedDouble);
        System.out.println("Decoded Double String: " + decodedDoubleString);

        String input = "Hello, World!";
        
        // 使用CodeUtil工具将字符串编码为char[]
        char[] encodedChars = CodeUtil.encodeToCharArray(input);
        
        // 打印编码后的char数组
        System.out.println("Encoded char array:");
        for (char c : encodedChars) {
            System.out.print(c + " ");
        }
    }
}

输出:

text 复制代码
Encoded Double: 123.45
Decoded Double String: 123.45

Encoded char array:
H e l l o ,   W o r l d !

【2】使用对象池: 在某些业务场景中,可能存在大量完全相同或相似的对象。位了减少这些对象的内存占用,可以使用对象池技术来重用这些对象。通过对象池,我们可以确保相同的对象在内存中只有一份拷贝,从而节省大量的内存空间。需要注意的是,在使用对象池技术时,需要确保对象的正确性和线程安全性。

【3】限制容器最大容量: HashMap等容器类在Java中广泛使用,但他们没有容量上限。如果不断向容器中添加元素,它们会持续扩容以及容纳更多的元素,从而导致内存占用不断增加。为了避免这种情况的发生,我们使用具有容量限制的容器类(如Guava Cache)来替代HashMap等无限制容器。这样可以将内存占用控制在一个合理范围内,防止内存膨胀现象的发生。需要注意的是,在设置容量限制时,需要根据实际业务需求和系统性能进行合理权衡。

相关推荐
大灰狼191314 分钟前
【怎样基于Okhttp3来实现各种各样的远程调用,表单、JSON、文件、文件流等待】
java·springboot·网络流·okhttp3·文件流传输
当归10242 小时前
在java后端发送HTTPClient请求
java·开发语言
吾爱星辰2 小时前
【Kotlin 集合概述】可变参数vararg、中缀函数infix以及解构声明(二十)
java·开发语言·jvm·kotlin
吾爱星辰2 小时前
【Kotlin基于selenium实现自动化测试】初识selenium以及搭建项目基本骨架(1)
java·开发语言·jvm·selenium·kotlin
后台技术汇2 小时前
SpringCloud源码:服务端分析(二)- EurekaServer分析
java·spring boot·后端·spring·spring cloud
那你为何对我三笑留情2 小时前
二、Spring Boot集成Spring Security之实现原理
java·spring boot·spring·spring security
彭于晏6892 小时前
Activity
android·java·android-studio
java_heartLake2 小时前
设计模式之迭代器模式
java·设计模式·迭代器模式
不会编程的程序員2 小时前
C++中set集合和Python中set集合的区别
java·c++·python
影子落人间2 小时前
JAVA设计模式之策略模式
java·设计模式·策略模式