【问题标题】:Memory allocation when creating new String as literal/new Object将新字符串创建为文字/新对象时的内存分配
【发布时间】:2020-06-27 06:19:18
【问题描述】:

代码:

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。

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

第一行确实在池中创建了一个新的字符串类型对象。第二行确实将它作为常规对象在堆上创建

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

所以 s = "something" 在池上创建了一个新的 String 类型对象以及一个保存数据的底层数组 - 它也在池上创建。

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

我的理解正确吗?

注意:这不是关于常量池存储在哪里、实习生如何工作、创建了多少对象或使用不同的创建技术/使用实习生如何影响 == 结果的问题。

【问题讨论】:

  • JVM 不会尝试对数据进行“重复数据删除”。
  • @ch 没有完全得到您对上下文的回答,请您再详细一点。谢谢

标签: java string string-literals


【解决方案1】:

你用的是什么java版本?我在 11 jdk 上检查它,String.value 的类型是 char[]。后面的所有词都基于 11 JDK 版本。

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

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

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

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

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);

【讨论】:

  • 这实际上是有道理的。谢谢你的提示。从来没有真正想过查看构造函数:(所以基本上,要创建的第一个对象是在池中-> 作为原始对象传入的对象?然后根据文字/新关键字,JVM 分配或不分配内存位置一般堆?
  • 顺便说一句,我正在使用 Open jdk amazon-corretto 11。我见过使用 byte[] 的 open jdk 和使用 char[] 的 oracle。我必须对此进行更多研究,我认为这与压缩有关 - UTF-1 和 Latin1 编码。将不得不阅读有关此的更多信息
  • 我采取了另一种方式对此进行了一些研究。我将生成字节码并尝试找出更多。稍后我会将我的理解作为答案发表。
猜你喜欢
  • 2017-10-18
  • 1970-01-01
  • 1970-01-01
  • 2013-04-15
  • 2016-03-10
  • 1970-01-01
  • 1970-01-01
  • 2017-10-29
  • 2012-06-17
相关资源
最近更新 更多