杰瑞科技汇

Java字符串到底占几个字节?

这是一个非常经典且重要的问题,但答案并不是一个固定的数字。一个 Java 字符串(String)所占用的字节数取决于三个核心因素:

Java字符串到底占几个字节?-图1
(图片来源网络,侵删)
  1. 字符编码:这是最关键的因素,不同的编码方式会用不同数量的字节来表示同一个字符。
  2. :字符串中包含的字符本身,不同字符(如英文字母 'A' 和中文字 '中')在同一个编码下占用的字节数可能不同。
  3. JVM 实现和内存布局:对象头、对齐填充等也会占用额外的内存。

下面我们详细分解这三个因素,并给出计算方法和示例。


核心因素:字符编码

Java 源代码中的字符串是 Unicode 字符,但在内存中或写入文件/网络时,必须被编码成字节序列,最常见的编码有:

编码方式 描述 英文字母 (如 'A') 中文字符 (如 '中') Emoji (如 '😊')
ISO-8859-1 (Latin-1) 单字节编码,不支持中文 1 字节 1 字节 (会乱码,无法表示) 1 字节 (会乱码)
GBK / GB2312 中国国家标准编码,支持中文 1 字节 2 字节 通常不支持,会乱码
UTF-16 Java 内部默认使用的编码,固定 2 或 4 字节 2 字节 2 字节 4 字节 (辅助平面字符)
UTF-8 目前互联网上最流行的变长编码,兼容 ASCII 1 字节 3 字节 4 字节

在讨论字符串字节大小时,必须明确是在哪种编码下


字符串中包含的字符类型决定了在特定编码下的字节数。

Java字符串到底占几个字节?-图2
(图片来源网络,侵删)
  • ASCII 字符:在 UTF-8GBK 下通常只占 1 字节。
  • 常用中文字符:在 GBK 下占 2 字节,在 UTF-8 下占 3 字节。
  • Emoji 或生僻汉字:在 UTF-8 下可能占 4 字节。

JVM 对象内存开销

一个 Java String 对象不仅仅是字符数组,它还有一些额外的固定开销。

在主流的 64 位 JVM(使用压缩指针 -XX:+UseCompressedOops 的情况下)中,一个普通对象的内存布局大致如下:

  • 对象头:通常是 12 字节(用于存储对象的元数据、哈希码、锁信息等)。
  • 引用字段:指向 char[] 数组的引用本身占 4 字节(因为压缩指针)。
  • 实例数据:对于 Stringprivate final char[] value; 这个数组本身。
  • 对齐填充:JVM 要求对象的大小必须是 8 字节的倍数,如果不足则需要填充。

一个空字符串 至少也会占用 12 (对象头) + 4 (value引用) = 16 字节,并且已经对齐,无需填充。


如何计算一个字符串的字节数?

我们可以通过两种方式来计算:

理论计算(推荐用于理解)

总字节数 = 对象头 + value引用 + 数组长度 + 字符内容字节数 + 数组对齐填充

让我们用一个例子来计算:String str = "ABC中";

  1. 确定编码和字符字节数

    • 'A', 'B', 'C' 是 ASCII 字符,在 UTF-8 下各占 1 字节。
    • '中' 是中文字符,在 UTF-8 下占 3 字节。
    • 字节数 = 1 + 1 + 1 + 3 = 6 字节
  2. 计算 char[] 数组的内存占用

    • 数组对象头:12 字节。
    • 数组长度 (int 类型):4 字节。
    • 数组数据:char[] 本身,在 Java 中,char 类型是 2 字节的,char[4] 数组的数据部分占 4 * 2 = 8 字节。
    • char[] 数组总大小 = 12 (头) + 4 (长度) + 8 (数据) = 24 字节,这个大小已经是 8 的倍数,无需对齐填充。
  3. 计算 String 对象本身的内存占用

    • String 对象头:12 字节。
    • value 引用:4 字节。
    • String 对象本身大小 = 12 + 4 = 16 字节,这个大小也已经是 8 的倍数,无需对齐填充。
    • String 对象本身占用 16 字节
    • 它内部的 char[] 数组占用 24 字节
    • 整个 String 对象在堆上总共占用 16 + 24 = 40 字节

    注意:这里的 40 字节是 JVM 对象的内存占用,它包含了 char[] 中未编码的 Unicode 字符,如果你想问这个字符串在 UTF-8 编码下会生成多少个字节,那么答案是 6 字节

代码实践(推荐用于验证)

你可以通过 getBytes() 方法来获取特定编码下的字节数。

public class StringSizeExample {
    public static void main(String[] args) {
        String str = "ABC中";
        // 1. 获取不同编码下的字节数
        System.out.println("--- 字符串内容在不同编码下的字节数 ---");
        try {
            System.out.println("UTF-8: " + str.getBytes("UTF-8").length + " 字节"); // 1+1+1+3 = 6
            System.out.println("GBK: " + str.getBytes("GBK").length + " 字节");   // 1+1+1+2 = 5
            System.out.println("UTF-16: " + str.getBytes("UTF-16").length + " 字节"); // 2+2+2+2 = 8 (注意UTF-16有BOM头,可能为10)
            System.out.println("ISO-8859-1: " + str.getBytes("ISO-8859-1").length + " 字节"); // 1+1+1+1 = 4 (中文会乱码)
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 2. 估算JVM中对象的内存占用 (使用工具类)
        // 注意:这需要引入第三方库,如 org.openjdk.jol
        // 这里只展示概念,不直接运行
        /*
        import org.openjdk.jol.vm.VM;
        System.out.println("\n--- JVM中对象的内存布局 (使用JOL工具) ---");
        System.out.println(VM.current().details(str));
        */
        // JOL 输出会类似这样,证实我们的理论计算:
        // java.lang.String object internals:
        //  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
        //       0    12        (object header)                           N/A
        //      12     4   char[] String.value                           N/A
        //      16     4    int String.hash                              N/A
        //      20     1    byte String.coder                             N/A
        //      21     1    byte String.flags                            N/A
        //      24     4        (loss due to the next object alignment)
        // Instance size: 24 bytes
        // Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
        // 注意:JOL 的结果可能因JVM版本和参数而异,但总大小通常在 40-48 字节左右。
    }
}
问题场景 答案 示例 ("ABC中")
在 UTF-8 编码下,字符串有多长? 等于所有字符按 UTF-8 编码后的字节数之和。 6 字节
在 GBK 编码下,字符串有多长? 等于所有字符按 GBK 编码后的字节数之和。 5 字节
在 JVM 中,一个 String 对象占用多少内存? 这是一个固定开销(约 16-24 字节)加上其内部 char[] 数组的内存(约 24 字节起),总计通常在 40-48 字节 左右,与内容无关。 40 字节

当别人问 "Java String 几个字节" 时,最严谨的回答是:“这取决于编码方式和字符串内容,在 UTF-8 编码下,一个英文字符占 1 字节,一个中文字符通常占 3 字节,JVM 对象本身还有额外的固定内存开销。”

分享:
扫描分享到社交APP
上一篇
下一篇