英文:
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 = False,arr1 == 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);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论