博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
内存优化篇-String/char[]/byte[]的选择
阅读量:6454 次
发布时间:2019-06-23

本文共 3870 字,大约阅读时间需要 12 分钟。

Java基本数据类型的大小

type

size(bits)

bytes

boolean

8

1

byte

8

1

char

16

2

short

16

2

int

32

4

long

64

8

float

32

4

double

64

8

Java引用的大小

在 32 位的 JVM 上,一个对象引用占用 4 个字节;在 64 位JVM上,占用 8 个字节。

使用 8 个字节是为了能够管理大于 4G 的内存,如果你的程序不需要访问大于 4G 的内存,

可通过 -XX:+UseCompressedOops 选项,开启指针压缩。从 Java 1.6.0_23 开始,这个选项默认是开的。

Java对象头的大小

在32位JVM中,对象头的大小为8个字节(4字节的Mark Word+4字节的Klass Pointer).

在64位JVM上,占用16个字节(8字节的Mark Word+8字节的Klass Pointer),因为开启UseCompressedOops,所以实际占用12个字节(8字节的Mark Word+4字节的Klass Pointer) 。参考

接下来的内容都基于64位的JVM来展开

Java对象的大小

1、任意Java对象都包含至少12个字节的Object Header。

2、JVM分配内存以8字节为基本单位,如果不满小于8字节,则向8字节的倍数补齐。参考

思考   

Object object = new Object(); 占用多少内存?

数组的大小如何计算?

验证

添加Maven依赖

 
org.openjdk.jol
jol-core
0.9
 
import org.openjdk.jol.info.ClassLayout;/** * Created by jianpingpan on 2019/1/17. */public class BasicClass {    public static void main(String[] args) throws Exception {        System.out.println(ClassLayout.parseClass(Object.class).toPrintable());        System.out.println(ClassLayout.parseClass(String.class).toPrintable());        System.out.println(ClassLayout.parseClass(byte[].class).toPrintable());        System.out.println(ClassLayout.parseClass(char[].class).toPrintable());    }}

1547711148456-a226b348-168e-45ed-9265-52

byte[] 和char[]的 object header为16个字节是因为有4个字节的数组长度。

String /  char[]   / byte[] 内存大小计算

String a = new String("abc");    String b = new String("abcd");    String c = new String("abc");

1547713532558-2389799f-3057-4ebd-bae0-11

第一行占用JVM内存的大小:

对象大小 = 12字节(object header)+

                   4字节 (hash)+

                   4字节(数组引用vlaue[]) +

                   4字节 (padding)

                   16字节+3*2字节+2字节padding   (数组value[])

              =   48字节

假设要缓存的字符个数为N。

String的内存大小计算公式 = 40+N*2  +padding

char数组的内存大小计算公式 = 16+N*2+padding

如果用byte数组来存储字符串数据,占用的内存大小X需要分2种情况讨论:

1、如果需要存储的字符全在ASCII码中,一个字符用一个byte就可以存储 (编码方式可选ISO-8859-1/GBK/UTF-8):

X = 16+N+padding

2、如果需要存储的字符范围不能被ASCII码覆盖,则需要根据字符范围确定合适的存储方式。

如需要要存储字符集为ASCII+中文字符,则可使用GBK编码:

16+N+padding <X <  16+N*2+padding

如果字符集不能被ASCII码覆盖,并且包含非中文字符,则使用UTF-8编码:

16+N+padding<X<16+6*N+padding

结论:

由此可见,char数组占用的内存大小小于String占用的内存大小。

若存储的字符范围以ASCII码为主,使用byte数组存储优于char数组。

实际使用场景

那么在缓存中可以直接用char[]或byte[]替换String么?          

Set<String> set = new HashSet<>();

替换成

Set<byte[]> set = new HashSet<>();

会怎样呢?

很明显,contains方法、get方法都会失效。因为每个byte[]的hashCode不一样。

我们用下面的这个ByteArray/CharArray封装byte[],再用ByteArray替换String。

/** * Created by jianpingpan on 2019/1/23. */public class ByteArray {    byte[] bytes;    public ByteArray(byte[] bytes){        this.bytes = bytes;    }    @Override    public int hashCode() {        if(null == bytes){            return 0;        }        return new String(bytes).hashCode();    }    @Override    public boolean equals(Object obj) {        if(obj == null){            return false;        }        return hashCode()==obj.hashCode();    }}

(CharArray的实现方式同ByteArray,只是把byte[] bytes 替换成 char[] chars即可)

ByteArray占用的内存大小 = 

                  12字节(object header+

                   4字节(数组引用bytes[]) +

                   16字节+N字节+padding   (数组bytes[])

              =   32字节+N字节+padding

CharArray占用的内存大小=

                   12字节(object header+

                   4字节(数组引用bytes[]) +

                   16字节+2*N字节+padding   (数组bytes[])

              =   32字节+2*N字节+padding

其中,N为数组中元素的个数。

例子

以存储100万条长度为32位的MD5字符串为例且内容互不相同字符串为例(假设字符串中的字符均为字母、数字、下划线)。

 

可以用来计算内存使用量 。

  

import com.javamex.classmexer.MemoryUtil;/** * Created by jianpingpan on 2019/1/25. */public class StringTest {    public static void main(String[] args){                String s="cfcd208495d565ef66e7dff9f98764da";        ByteArray b = new ByteArray(s.getBytes());        CharArray c = new CharArray(s.toCharArray());        long stringBytes = MemoryUtil.deepMemoryUsageOf(s);        long byteArrayBytes = MemoryUtil.deepMemoryUsageOf(b);        long charArrayBytes = MemoryUtil.deepMemoryUsageOf(c);        System.out.println("stringBytes:"+stringBytes);        System.out.println("byteArrayBytes:"+byteArrayBytes);        System.out.println("charArrayBytes:"+charArrayBytes);    }}

resize,w_2000

 

用String存储,每条记录占用的空间为 40+32*2 = 104字节

用ByteArray存储,每条记录占用的空间为 32+32 = 64字节

用CharArray存储,每条记录占用的空间为 32+32*2 = 96字节

                  

参考文档:

转载地址:http://sefzo.baihongyu.com/

你可能感兴趣的文章
自定义 启动和关闭 oracle 的命令
查看>>
SQLite数据库、ListView控件的使用
查看>>
Quartz
查看>>
正则表达式介绍
查看>>
初识Scala反射
查看>>
第三十九天
查看>>
Redis详解
查看>>
论程序员加班的害处
查看>>
codeblocks快捷键
查看>>
基于HTML5的WebGL设计汉诺塔3D游戏
查看>>
WPF资料链接
查看>>
过滤DataTable表中的重复数据
查看>>
Oracle数据库-trunc函数的用法
查看>>
prepare for travel 旅行准备
查看>>
再次更新
查看>>
iOS开发代理(委托)模式详解
查看>>
微服务学习笔记二:Eureka服务注册发现
查看>>
C# 获取编码
查看>>
mysql的数据类型int、bigint、smallint 和 tinyint取值范围
查看>>
利用网易获取所有股票数据
查看>>