创建新字符串作为文字/新对象时的内存分配

huangapple go评论77阅读模式
英文:

Memory allocation when creating new String as literal/new Object

问题

代码

    String s1 = "Hello";
    String s2 = new String("Hello");
    		
    Field declaredField = String.class.getDeclaredField("value");
    declaredField.setAccessible(true);
    		
    byte[] arr1 = (byte[]) declaredField.get(s1);
    byte[] arr2 = (byte[]) declaredField.get(s2);


现在s1 == s2 = Falsearr1 == arr2 = True

我的问题是对象是如何存储在内存中的

第一行在池中创建了一个新的String类型对象第二行将其作为常规对象在堆上创建

但是底层的byte[]数组是相同的这让我想到JVM以某种方式进行检查如果已经存在这样的byte[]它会将所有引用指向同一个数组这是否意味着底层数组在池中被缓存无论是使用字面量还是new关键字

因此s = "something"会在池中创建一个新的String类型对象以及一个包含数据的底层数组 - 这个数组也是在池中创建的

`s = new String("something")` -> 在堆上创建String类型对象但底层数组仍存储在池中/如果已经存在则只是创建一个引用

我的理解正确吗

这不是关于常量池存储在哪里intern的工作原理创建了多少对象以及如何使用不同的创建技术/使用intern如何影响==结果的问题
英文:

Code:

String s1 = "Hello";
String s2 = new String("Hello");
		
Field declaredField = String.class.getDeclaredField("value");
declaredField.setAccessible(true);
		
byte[] arr1 = (byte[])declaredField.get(s1);
byte[] arr2 = (byte[])declaredField.get(s2);

Now, s1 == s2 = False, arr1== arr2 = True.

My question is how are the objects being stored in the memory.

First line does create a new String type object on the pool. Second line does create it on the heap as a regular object

But the underlying byte[] array is the same. This makes me think, somehow JVM does this check if such an existing byte[] already exists and points all references to the same array. Does this mean the underlying array is cached in the pool, no matter we use the literal or new keyword?

So s = "something" creates a new String type object on the pool along with a underlying array holding the data - which is also created on the pool.

s = new String("something") -> creates the String type object on the heap, but the underlying array still stored in the pool/if already exists, a reference is just created.

Is my understanding correct?

Note: this is not a question about where is the constant pool stored, how intern works, how many objects are created or how using a different creation technique/using intern affects the == results.

答案1

得分: 2

什么Java版本是您在使用的?我在11 JDK上进行了检查,String.value的类型是char[]。所有接下来的内容都基于11 JDK版本。

这种行为的原因是String构造函数。

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

正如您所见,value字段是从original String对象复制而来的。由于您在new String(...)参数中使用了"Hello",因此"Hello"是从字符串池中获取的,并传递给构造函数,其中value字段被复制到新的String对象中。

您可以稍微更改代码以达到预期的行为:

String s1 = "Hello";
String s2 = new String("Hell") + "o";
Field declaredField = String.class.getDeclaredField("value");
declaredField.setAccessible(true);
char[] arr1 = (char[]) declaredField.get(s1);
char[] arr2 = (char[]) declaredField.get(s2);
assert arr1 != arr2;
assert s1.equals(s2);
英文:

What java version do you use? I check it on 11 jdk and type of String.value is char[]. All next words based on 11 JDK version.

The reason for this behavior is a String constructor.

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

As you can see, the value field is copied from original String object. Since you are using "Hello" for new String(...) argument, "Hello" is gotten from the String pool and passed to the constructor, where the value field is copping to a new String object.

You can change your code a bit for reaching expected behaviour:

String s1 = "Hello";
String s2 = new String("Hell") + "o";
Field declaredField = String.class.getDeclaredField("value");
declaredField.setAccessible(true);
char[] arr1 = (char[])declaredField.get(s1);
char[] arr2 = (char[])declaredField.get(s2);
assert arr1 != arr2;
assert s1.equals(s2);

huangapple
  • 本文由 发表于 2020年3月16日 13:41:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/60700853.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定