java学习-内存分配-特殊的String
import lombok.extern.slf4j.Slf4j; /** * JVMStringDemo * 字符串字面数据处理过程 * * @author guoxing * @date 2020/10/21 10:00 AM * @since */ @Slf4j public class JVMStringDemo { public static final String CONSTANT_STRING = "a"; public static final String CONSTANT_STRING_B = "b"; public static final String CONSTANT_STRING_C = "c"; // 对于 静态变量 在编译时 就直接被解析为最终结果 public static final String CONSTANT_CONTACT = CONSTANT_STRING + CONSTANT_STRING_B; // 由于 对于常量 CONSTANT_STRING_B 和 CONSTANT_STRING_C 是固定的,因此对于其得到的结果也是固定的,所以在constant-pool中会存在其结果数据 public String property = CONSTANT_STRING_B + CONSTANT_STRING_C; public static void main(String[] args) { /** * 对于 String#intern 的作用一般 对于 new String 对象才有用, * 对于 一个 new String 对象是存储在 heap中的;而对于字符串字面量是在编译阶段已经存储在 constant pool 区域中了, * 对于 String obj = new String("字面量"); 其结构实际为 在 heap 中 存在一个String 对象数据 内部又引用了 Constant pool 中字面量数据地址 * 当对一个 new 得到的String 对象,当其引用调用了intern 方法时,实际当前引用会指向 constant pool 中 已存在常量数据 * */ String b = "b"; log.info("string constant id:{}", System.identityHashCode(b)); verify(); } public static void verify() { String newString = new String("c");// 对于该行代码,首先在编译加载期间,首先会在constant pool 中生成 字符串数据 "c";当在运行时,会在堆中创建String对象,并在栈中压入引用 log.info("new string id:{}", System.identityHashCode(newString));//这里获取到的是堆中String对象地址 String intern = newString.intern(); log.info("string intern id:{}", System.identityHashCode(intern)); // 由于在constant pool 中存在 该字符串数据,因此该处实际得到的是constant pool 中的对象地址 intern = "c"; log.info("string intern id:{}", System.identityHashCode(intern)); // 这里是直接引用的 constant pool 中的内存地址 // 在constant pool 中 是不会存在 "ab"的;"ab" 是 运行时的结果; // 对于变量c以及其结果是直接存放在栈中的 // 对于 c.intern 中, // 首先会执行lookup_shared 来查找堆中-constant pool中是否存在该字符串,如果存在,则会直接返回constant pool 中的内存地址; // 反之,则会将当前字符串数据写入到constant-pool中并返回constant-pool中的地址 String a = "a"; String b = "b"; String c = a + b; log.info("running new result id:{}", System.identityHashCode(c)); log.info("running new result intern id:{};{}", System.identityHashCode(c.intern()), c == c.intern()); // false } /** * 对于当前java中存在字符串字面量数据 "a"和"b" * 在 java.lang.String.java中 存在注释 * The {@code String} class represents character strings. * All string literals in Java programs, such as {@code "abc"}, are implemented as instances of this class. // 对于"abc"等这种字符串字面量实际就是当前类型的实例 * Strings are constant; // 字符串是一个常量 * 对于 "are implemented as instances of this class" 这句话的实际是体现在前端编译阶段(编译为class文件) * 以下为当前 class文件的 编译结果 * public class com.xing.level.JVMStringDemo * minor version: 0 * major version: 59 * flags: (0x0021) ACC_PUBLIC, ACC_SUPER * this_class: #9 // com/xing/level/JVMStringDemo * super_class: #2 // java/lang/Object * interfaces: 0, fields: 1, methods: 2, attributes: 1 * Constant pool: * #1 = Methodref #2.#3 // java/lang/Object."<init>":()V * #2 = Class #4 // java/lang/Object * #3 = NameAndType #5:#6 // "<init>":()V * #4 = Utf8 java/lang/Object * #5 = Utf8 <init> * #6 = Utf8 ()V * #7 = String #8 // b * #8 = Utf8 b // 字符串字面量 * #9 = Class #10 // com/xing/level/JVMStringDemo * #10 = Utf8 com/xing/level/JVMStringDemo * #11 = Utf8 CONSTANT_STRING * #12 = Utf8 Ljava/lang/String; * #13 = Utf8 ConstantValue * #14 = String #15 // a * #15 = Utf8 a //字符串字面量 * #16 = Utf8 Code * #17 = Utf8 LineNumberTable * #18 = Utf8 LocalVariableTable * #19 = Utf8 this * #20 = Utf8 Lcom/xing/level/JVMStringDemo; * #21 = Utf8 main * #22 = Utf8 ([Ljava/lang/String;)V * #23 = Utf8 args * #24 = Utf8 [Ljava/lang/String; * #25 = Utf8 SourceFile * #26 = Utf8 JVMStringDemo.java * { * public static final java.lang.String CONSTANT_STRING; * descriptor: Ljava/lang/String; * flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL * ConstantValue: String a * * public com.xing.level.JVMStringDemo(); * descriptor: ()V * flags: (0x0001) ACC_PUBLIC * Code: * stack=1, locals=1, args_size=1 * 0: aload_0 * 1: invokespecial #1 // Method java/lang/Object."<init>":()V * 4: return * LineNumberTable: * line 14: 0 * LocalVariableTable: * Start Length Slot Name Signature * 0 5 0 this Lcom/xing/level/JVMStringDemo; * * public static void main(java.lang.String[]); * descriptor: ([Ljava/lang/String;)V * flags: (0x0009) ACC_PUBLIC, ACC_STATIC * Code: * stack=1, locals=2, args_size=1 * 0: ldc #7 // String b * 2: astore_1 * 3: return * LineNumberTable: * line 18: 0 * line 19: 3 * LocalVariableTable: * Start Length Slot Name Signature * 0 4 0 args [Ljava/lang/String; * 3 1 1 b Ljava/lang/String; * } * 在 classLoad加载当前class文件转换为 class对象时, * 在 "src/hotspot/share/classfile/classFileParser.cpp"存在 * ClassFileParser对象实例化时会调用 * -> parse_stream 解析class二进制文件流数据 * -> parse_constant_pool 解析 constant_pool相关数据 * -> parse_constant_pool_entries 在该方法中会解析 class文件中的 Constant pool 中的数据,在这里会根据索引(#数值)来解析数据, 并且会使用数组存储当前数据 * CONSTANT_String_info { * u1 tag; // src/hotspot/share/utilities/constantTag.hpp 中定义的JVM_CONSTANT_ 相关数据 * u2 string_index; // 当前数据在 constant pool 的索引值 * } * -> 在后续的for 循环迭代时 ,当匹配到 JVM_CONSTANT_StringIndex 时,这里实际是存储字符串数据的引用数据 * */ }
可参考
The Java Virtual Machine Specification, Java SE 12 Edition 中的
第四章节 "The class File Format"
第五章节 "Loading, Linking, and Initializing"
java.lang.String#intern方法
/** * Returns a canonical representation for the string object. * <p> * A pool of strings, initially empty, is maintained privately by the * class {@code String}. * <p> * When the intern method is invoked, if the pool already contains a // 这里的pool 指的是constan-pool * string equal to this {@code String} object as determined by // 通过equals进行判断 * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the // 添加到constan-pool,并返回 constant-pool中的引用 * pool and a reference to this {@code String} object is returned. * <p> * It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. * <p> * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * <cite>The Java Language Specification</cite>. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. * @jls 3.10.5 String Literals */ public native String intern();
个人对String#intern 的理解,在一定程度上起到help gc的作用, 对于运行时生成存放到栈中的数据,本身存活时间较短;而对于通过 new 显示实例化存放到堆中的数据,可以通过统一指向到constant-pool,避免了heap中的重复数据(equals),从而实现heap中的内存回收
在jdk java.lang.String 中存在注释如下
* Strings are constant; their values cannot be changed after they * are created. String buffers support mutable strings. * Because String objects are immutable they can be shared. For example: * <blockquote><pre> * String str = "abc"; * </pre></blockquote><p> * is equivalent to: * <blockquote><pre> * char data[] = {'a', 'b', 'c'}; * String str = new String(data); * </pre></blockquote><p>
根据以上注释可以了解到对于通过 String a = "abc" ; 这种字面量赋值方式 实际也生成了对象的String 对象实例 ,不过这些操作是 jvm虚拟机在后端编译阶段进行处理的; 对于String#value char数组(jdk8)而言,其字段的赋值是jvm进行执行的操作,而非类似于 String有参构造进行人工赋值