【问题标题】:Size of Initialisation string in javajava中初始化字符串的大小
【发布时间】:2011-11-30 08:36:08
【问题描述】:

显然 javac 中初始化字符串的大小是有限制的。谁能帮我确定最大限制是多少?

谢谢

编辑:

我们正在构建一个类似于“{1,2,3,4,5,6,7,8......}”的初始化字符串,但最好有 10,000 个数字。当我们为 1000 执行此操作时,它可以工作,10,000 会抛出一个错误,指出代码对于 try 语句来说太大。

为了产生这个,我们使用了一个字符串生成器并循环遍历一个附加值的数组。显然这是javac的一个限制。我们被告知,如果我们以小块的形式传递它,我们可以在我们正在调用的方法中重建数组。然而这是不可能的,因为我们无法控制我们正在调用的用户方法。

我想发布代码但不能,因为这是大学的项目。我不是在寻找代码解决方案,只是帮助理解这里的实际问题。

它的for循环是罪犯

    Object o = new Object() 
    { 
        public String toString() 
        { 
            StringBuilder s = new StringBuilder();
            int length = MainInterfaceProcessor.this.valuesFromData.length;
            Object[] arrayToProcess = MainInterfaceProcessor.this.valuesFromData;

            if(length == 0)
            {
                //throw exception to do
            }
            else if(length == 1)
            {
                s.append("{" + Integer.toString((Integer)arrayToProcess[0])+"}");
            }
            else
            {
                s.append("{" + Integer.toString((Integer)arrayToProcess[0])+","); //opening statement
                for(int i = 1; i < length; i++)
                {
                    if(i == (length - 1))
                    {
                        //last element in the array so dont add comma at the end
                        s.append(getArrayItemAsString(arrayToProcess, i)+"}");
                        break;
                    }       
                    //append each array value at position i, followed
                    //by a comma to seperate the values
                    s.append(getArrayItemAsString(arrayToProcess, i)+ ",");
                }
            }
            return s.toString();
        }
    };
    try 
    {
        Object result = method.invoke(obj, new Object[] { o });

}

【问题讨论】:

  • 你到底是什么意思,命令行的长度?命令行参数?字符串的最大大小?为什么这很重要?
  • 因为我正在为一个名为 bluej 的软件(基于学生的 ide)开发插件。要远程调用方法,您必须构建一个初始化字符串,然后由我尝试使用数组调用的方法对其进行解析。大型数组失败,因为初始化字符串太大。我只是想不知道如何查看限制
  • 你指的是this之类的东西吗?
  • 啊,你说的是数组初始化,而不是字符串?

标签: java compiler-construction javac


【解决方案1】:

String 字面量(即"...")的长度受类文件格式的CONSTANT_Utf8_info 结构的限制,该结构由CONSTANT_String_info 结构引用。

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}

这里的限制因素是 length 属性,它只有 2 个字节大,即最大值为 65535。 这个数字对应于字符串的修改后的 UTF-8 表示形式的字节数(这实际上几乎是CESU-8,但 0 字符也以两字节的形式表示)。

因此,纯 ASCII 字符串文字最多可以包含 65535 个字符,而由 U+0800 ...U+FFFF 范围内的字符组成的字符串只有三分之一。 UTF-8 中编码为代理对的那些(即 U+10000 到 U+10FFFF)每个占用 6 个字节。

(标识符也有相同的限制,即类、方法和变量名,以及它们的类型描述符,因为它们使用相同的结构。)

Java 语言规范没有提到对string literals 的任何限制:

字符串文字由零个或多个用双引号括起来的字符组成。

因此,原则上,编译器可以将较长的字符串文字拆分为多个CONSTANT_String_info 结构,并在运行时通过连接(以及.intern()-ing 结果)重建它。我不知道是否有任何编译器实际上正在这样做。


这表明问题与字符串文字无关,而是与数组初始化器有关。

当将对象传递给BMethod.invoke(类似于 BConstructor.newInstance)时,它可以是 BObject(即现有对象的包装器,然后它将传递包装的对象),字符串(将按原样通过)或其他任何内容。在最后一种情况下,对象将被转换为字符串(通过toString()),然后将该字符串解释为Java 表达式。

为此,BlueJ 将把这个表达式包装在一个类/方法中并编译这个方法。在该方法中,数组初始值设定项被简单地转换为一长串数组赋值......这最终使该方法比 Java 方法的 maximum bytecode size 更长:

code_length 项的值必须小于 65536。

这就是为什么它会因为更长的数组而中断。


因此,要传递更大的数组,我们必须找到其他方法将它们传递给 BMethod.invoke。 BlueJ 扩展 API 无法创建或访问包装在 BObject 中的数组。

我们在聊天中发现的一个想法是这样的:

  1. 在项目内(或在新项目中,如果它们可以互操作)创建一个新类,如下所示:

    public class IntArrayBuilder {
        private ArrayList<Integer> list;
        public void addElement(int el) {
            list.add(el);
        }
        public int[] makeArray() {
            int[] array = new int[list.size()];
            for(int i = 0; i < array.length; i++) {
               array[i] = list.get(i);
            }
            return array;
        }
    }
    

    (这是用于创建int[]的情况 - 如果您也需要其他类型的数组,它可以 也变得更通用。此外,它可以通过使用 内部int[] 作为存储,随着它的增长偶尔扩大它,以及 int makeArray 做一个最终的arraycopy。这是一个草图,因此这是最简单的实现。)

  2. 从我们的扩展中,创建这个类的一个对象, 并通过调用其.addElement 方法向该对象添加元素。

    BObject arrayToBArray(int[] a) {
        BClass builderClass = package.getClass("IntArrayBuilder");
        BObject builder = builderClass.getConstructor(new Class<?>[0]).newInstance(new Object[0]);
        BMethod addMethod = builderClass.getMethod("addElement", new Class<?>[]{int.class});
        for(int e : a) {
            addMethod.invoke(builder, new Object[]{ e });
        }
        BMethod makeMethod = builderClass.getMethod("addElement", new Class<?>[0]);
        BObject bArray = (BObject)makeMethod.invoke(builder, new Object[0]);
        return bArray;
    }
    

    (为了提高效率,BClass/BMethod 对象实际上可以检索一次并缓存,而不是每次数组转换一次。)
    如果您通过某种算法生成数组内容,则可以在此处进行此生成,而不是先创建另一个包装对象。

  3. 在我们的扩展中,使用长数组调用我们真正想要调用的方法,并传递我们的包装数组:

    Object result = method.invoke(obj, new Object[] { bArray });
    

【讨论】:

  • 您是否收到一些编译器错误消息? (将其添加到问题中。)您使用的是哪个编译器?您可以尝试使用 StringBuilder 或 .concat() 手动组合两个(或更多)部分的字符串。
【解决方案2】:

字符串长度受Integer.MAX_VALUE的限制

【讨论】:

    【解决方案3】:

    如果类型是数字最大长度 = Integer.MAX_VALUE,如果类型是字符最大长度 = 65536

    【讨论】:

      猜你喜欢
      • 2011-03-20
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 2014-02-08
      • 2023-03-16
      • 2016-04-02
      • 2013-07-05
      相关资源
      最近更新 更多