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有参构造进行人工赋值

posted @ 2020-10-21 17:03  郭星  阅读(104)  评论(0编辑  收藏  举报