写在前面

最近小伙伴加群时,我总是问一个问题:Java中的String类占用多大的内存空间?很多小伙伴的回答着实让我哭笑不得,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道的,更让人哭笑不得的是竟然还有人说是2的31次方。那如果真是这样的话,服务器的内存空间还放不下一个字符串呀!作为程序员的我们,可不能闹这种笑话呀。今天,我们就一起来聊聊Java中的String到底占用多大的内存空间!

Java对象的结构

首先,我们来下Java对象在虚拟机中的结构,这里,以HotSpot虚拟机为例。

注:图片来源http://r6d.cn/wp7q

从上面的这张图里面可以看出,对象在内存中的结构主要包含以下几个部分:

  • Mark Word(标记字段):对象的Mark Word部分占4个字节,其内容是一系列的标记位,比如轻量级锁的标记位,偏向锁标记位等等。
  • Klass Pointer(Class对象指针):Class对象指针的大小也是4个字节,其指向的位置是对象对应的Class对象(其对应的元数据对象)的内存地址
  • 对象实际数据:这里面包括了对象的所有成员变量,其大小由各个成员变量的大小决定,比如:byte和boolean是1个字节,short和char是2个字节,int和float是4个字节,long和double是8个字节,reference是4个字节
  • 对齐:最后一部分是对齐填充的字节,按8个字节填充。

换种说法就是:

  • 对象头(object header):8 个字节(保存对象的 class 信息、ID、在虚拟机中的状态)
  • Java 原始类型数据:如 int, float, char 等类型的数据
  • 引用(reference):4 个字节
  • 填充符(padding)

Java中的String类型

空String占用的空间

这里,我们以Java8为例进行说明。首先,我们来看看String类中的成员变量。

/** 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;

在 Java 里数组也是对象,因此数组也有对象头。所以,一个数组所占的空间为对象头所占的空间加上数组长度加上数组的引用,即 8 + 4 + 4= 16 字节 。

所以,我们可以得出一个空String对象所占用的内存空间,如下所示。

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

所以,小伙伴们,你们的回答正确吗?

非空String占用的空间

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

40 + 2 * n

其中,n为字符串的长度。

这里,可能有小伙伴会问,为什么是 40 + 2 * n 呢?这是因为40是空字符串占用的内存空间,这个我们上面已经说过了,String类实际上是把数据存储到char[]这个成员变量数组中的,而char[]数组中的一个char类型的数据占用2个字节的空间,所以,只是String中的数据就会占用 2 * n(n为字符串的长度)个字节的空间,再加上空字符串所占用的40个字节空间,最终得出一个字符串所占用的存储空间为: 40 + 2 * n (n为字符串长度)。

注:40 + 2 * n 这个公式我们可以看成是计算String对象占用多大内存空间的通用公式。

因此在代码中大量使用String对象时,应考虑内存的实际占用情况。

验证结论

接下来,我们就一起来验证下我们上面的结论。首先,创建一个UUIDUtils类用来生成32位的UUID,如下所示。

package io.mykit.binghe.string.test;

import java.util.UUID;

/**
* @author binghe
* @version 1.0.0
* @description 生成没有-的UUID
*/
public class UUIDUtils {
public static String getUUID(){
String uuid = UUID.randomUUID().toString();
return uuid.replace("-", "");
}
}

接下来,创建一个TestString类,在main()方法中创建一个长度为4000000的数组,然后在数组中放满UUID字符串,如下所示。

package io.mykit.binghe.string.test;

import java.util.UUID;

/**
* @author binghe
* @version 1.0.0
* @description 测试String占用的内存空间
*/
public class TestString{
public static void main(String[] args){
String[] strContainer = new String[4000000];
for(int i = 0; i < 4000000; i++){
strContainer[i] = UUIDUtils.getUUID();
System.out.println(i);
}
//防止程序退出
while(true){ }
}
}

这里,4000000个字符串,每个字符串的长度为32,所以保存字符串数据所占用的内存空间为:(40 + 32 * 2) * 4000000 = 416000000字节,约等于416MB。

我们使用Jprofiler内存分析工具进行分析:

可以看到,使用Jprofiler内存分析工具的结果为:321MB + 96632KB,约等于417MB。之所以使用Jprofiler内存分析工具得出的结果比我们计算的大些,是因为在程序实际运行的过程中,程序内部也会生成一些字符串,这些字符串也会占用内存空间!!

所以,使用Jprofiler内存分析工具得出的结果符合我们的预期。

好了,今天就到这儿吧,希望小伙伴们能有所收获,我是冰河,我们下期见!!

重磅福利

微信搜一搜【冰河技术】微信公众号,关注这个有深度的程序员,每天阅读超硬核技术干货,公众号内回复【PDF】有我准备的一线大厂面试资料和我原创的超硬核PDF技术文档,以及我为大家精心准备的多套简历模板(不断更新中),希望大家都能找到心仪的工作,学习是一条时而郁郁寡欢,时而开怀大笑的路,加油。如果你通过努力成功进入到了心仪的公司,一定不要懈怠放松,职场成长和新技术学习一样,不进则退。如果有幸我们江湖再见!

另外,我开源的各个PDF,后续我都会持续更新和维护,感谢大家长期以来对冰河的支持!!

Java中的String到底占用多大的内存空间?你所了解的可能都是错误的!!的更多相关文章

  1. Java中boolean类型到底占用多少字节

    虽然 Java 虚拟机定义了 boolean 这种数据类型,但是只对它提供了非常有限的支持.在 Java 虚拟机中没有任何供 boolean 值专用的字节码指令,在 Java 语言之中涉及到 bool ...

  2. Java中boolean类型到底占用多少字节(转载)

    来源:https://www.cnblogs.com/brucecloud/p/5509921.html 虽然 Java 虚拟机定义了 boolean 这种数据类型,但是只对它提供了非常有限的支持.在 ...

  3. 一个Java对象到底占用多大内存?

    最近在读<深入理解Java虚拟机>,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好 ...

  4. 一个Java对象到底占用多大内存

    在网上搜到了一篇博客讲的非常好,里面提供的这个类也非常实用: import java.lang.instrument.Instrumentation; import java.lang.reflect ...

  5. 关于JAVA中的String的使用与连接(转)

    JAVA中的String连接性能 Java中的String是一个非常特殊的类,使它特殊的一个主要原因是:String是不可变的(immutable).           String的不可变性是Ja ...

  6. Java中的String为什么是不可变的?

    转载:http://blog.csdn.net/zhangjg_blog/article/details/18319521 什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那 ...

  7. Java基础知识强化101:Java 中的 String对象真的不可变吗 ?

    1. 什么是不可变对象?       众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对 ...

  8. Java中的String为什么是不可变的? -- String源码分析

    众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是, ...

  9. Java中字符串string的数据类型

    Java中字符串string的数据类型 时间:2017-07-03 08:01:47 YuanMxy 原文:https://blog.csdn.net/YuanMxy/article/details/ ...

随机推荐

  1. 记录openssl和openssh升级中遇到的问题以及解决方法

    本文档讲述的升级操作是基于操作系统centos6.5,使用的openssl版本是openssl-1.0.2l.tar.gz,openssh版本是openssh-7.6p1.tar.gz. 1. 依赖检 ...

  2. JS 浏览器BOM

    BOM:Browser Object Model 浏览器对象模型 2.组成: window :窗口对象 1.创建: 2.方法: *与弹出框有关 1.alert(); 弹出警告框 2.confirm() ...

  3. springboot中图标的定制

    因为我用的版本是org.springframework.boot spring-boot-starter-parent 2.3.3.RELEASE 第一种方法: 配置一个application.yml ...

  4. 分布式系统监视zabbix讲解九之使用snmp监控windows

    zabbix监控windows演示 第一步:开启服务. 控制面板>程序>程序和功能>打开或关闭windows功能,勾选上简单网络管理协议(SNMP),以及下面的子程序.点击确定 第二 ...

  5. Redis底层数据结构详解

    上一篇说了Redis有五种数据类型,今天就来聊一下Redis底层的数据结构是什么样的.是这一周看了<redis设计与实现>一书,现来总结一下.(看书总是非常烦躁的!) Redis是由C语言 ...

  6. 企业项目实战 .Net Core + Vue/Angular 分库分表日志系统三 | 控制反转搭配简单业务

    教程预览 01 | 前言 02 | 简单的分库分表设计 03 | 控制反转搭配简单业务 04 | 强化设计方案 05 | 完善业务自动创建数据库 06 | 最终篇-通过AOP自动连接数据库-完成日志业 ...

  7. Redis学习(五)Redis知识点总结

    一.基础概念 Q:什么是 Redis? 定义:Redis 是完全开源免费基于内存亦可持久化的,遵守 BSD 协议,是一个高性能的 key-value 数据库. 特点: 数据的持久化 :可以将内存中的数 ...

  8. ch4inrulz: 1.0.1靶机渗透

    ch4inrulz: 1.0.1靶机渗透 扫描主机端口,还行啦四个开放的端口,8011和80端口都运行着web服务. 80端口下的robots.txt告诉我们什么都没有 在8011端口的apache服 ...

  9. Vue 表单拖拽排序

    Vue table表单拖拽 业务需求: 因为数据展示使用的是 elementUI 的 Table进行数据展示的,现在的需求是通过拖拽表单进行表单排序.同时,动态修改表单中的数据排列顺序.查阅了好多资料 ...

  10. Java面试题之计算字符/字符串出现的次数

    一.计算字符在给定字符串中出现的次数 二.计算字符串在给定字符串中出现的次数 1 import java.util.HashMap; 2 import java.util.Map; 3 4 publi ...