关于垃圾收集:Java高量的char [],如何减少?

Java high amount of char[], how to reduce?

我相信这个垃圾是在应用程序的各个地方调用new String时创建的。如何在不每次创建新对象的情况下"创建"字符串?

之所以成为这种垃圾敏感的原因是因为我的应用程序不能创建垃圾,因为我们需要用默认的Java GC实时运行。

1
2
3
4
5
6
7
8
9
10
11
// you can see I use the same chars array
public String getB37String() {
    long l = getLong();
    int i = 0;
    while (l != 0L) {
        long l1 = l;
        l /= 37L;
        chars[11 - i++] = validChars[(int) (l1 - l * 37L)];
    }
    return new String(chars, 12 - i, i);
}

例如,使用StringBuilder.toString(),下面使用new String

1
2
3
4
5
6
7
8
// and you can see that I use the same builder
public String getString() {
    builder.delete(0, builder.length());
    char ascii;
    while (0 != (ascii = (char) getUByte()) && backing.hasRemaining())
        builder.append(ascii);
    return builder.toString();
}


第一:观测

The reason for being this garbage-sensitive is because my application cannot create garbage as we need to run close to real-time with the default Java GC.

如果是这样("无法创建垃圾"实际上是statement1 a true),那么你有可能会开始在错误的地方在您的Java作为实现语言的采摘。

Java设计的假设是在一代的垃圾是好的。它是"成本"的固有的复杂性(和随之而来的网络错误显示内存管理)做这个假设pervades设计语言和标准库的设计。

另一件事是"Java是不支持它的是"良好的OO设计原则的强烈支持。特别是,有一些例外,漫游的API是提供强大的抽象设计的陷阱,可以在应用程序在处理您的休息。

例如,当你这样做:

1
2
3
  char[] c = new char[]{'a', 'b', 'c'};
  ...
  String s = new String(c);

一个新的String构造函数和拷贝allocates char[]c人物在靠近它。为什么?因为如果它不,你会有一个"漏抽象"。有人可以这样做:

1
2
3
4
5
  char[] c = new char[]{'a', 'b', 'c'};
  ...
  String s = new String(c);
  ...
  c[0] = 'd';

安切洛蒂和漏水的抽象变化进入一个不可变的对象(supposedly)。

那么什么是"解决方案"?

  • 你可以重写你的应用程序在C或C + +编程语言或其他一些有过在那里你可以完全控制内存分配。(当然,这是一个焊料的工作……有可能是其他原因,你为什么不能这样做。)

  • 你可以重新设计你的应用程序有关的部分,这样,他们不使用StringStringBuilder或任何标准的Java类,涉及显式或隐式(堆配置在罩)。这是不可能的,但它是很多的工作。例如,许多标准和第三方API,你的护照,他们String对象作为参数。

  • 你可以在你的代码分析部分做字符串操作是这样做"聪明的"分配,为了减少垃圾。

  • 不幸的是,所有这些事情都可能让你更大的代码库,读写硬,硬到调试和功能。

    案例1:一个它的真正的问题是,如果你真的试图解决的GC是休息。有地址的GC的方式来休息,不要去只要不产生任何垃圾。例如,撬一低GC暂停平行,和减少的大小的空间可以给年轻的一代,是你休息的noticeable短足以。另一个技巧是在A点的力气,当你的用户不知道,例如,当加载一个新的通知书;游戏中一级。


    如果您使用的是Java8u20或更高版本,您可以尝试使用-XX:+UseG1GC -XX:+UseStringDeduplication启用字符串重复数据消除。

    虽然这不能避免创建垃圾,但它可能会降低内存压力。

    如果您真的想在不复制char[]数组成本的情况下创建String实例,则必须通过反射访问包私有构造函数java.lang.String.String(char[], boolean)或私有char[] value字段,并进行适当的运行时检查/错误报告,以确定它是否实际工作。

    我不推荐,但这是一种选择。

    另一种选择是停止使用字符串并使用ByteBuffer。您可以根据需要对它们进行切片、返回视图、返回只读视图、回收它们。

    如果使用UTF-8数据,它们也会更紧凑。缺点是您不能使用需要字符串的API。

    或者在尽可能多的地方处理CharSequence/StringBuilder/CharBuffer对象。

    根据用例的不同,还可以为计算创建一个字符串缓存。Map,其中t是计算的输入参数。这样,对于EDOCX1的每个可能值(1),您只需要一个字符串。

    1
    return new String(chars, 12 - i, i);

    注意,对于Java 8,字符串不存储内部偏移,即字符串对象在某些较大的后备字符数组上不是"视图"。

    这在过去是不同的,但由于它是一个实现细节,所以它被改变了。

    通过bootstrap类加载器添加一个自定义字符串类可能会撤销这一更改,但这更有可能破坏或导致严重的性能下降。

    as we need to run close to real-time with the default Java GC.

    这可能是你的实际问题。

    默认情况下,配置的任何收集器都不会提供任何接近实时行为的内容。与串行或并行收集程序相比,CMS或G1可以提供更低的暂停时间,尤其是在大型堆上。


    两者之间的差异

    参考资料在这里。

    They both are the same, they are like any other object but:

    Since String is one of the most used type in any application, Java
    designer took a step further to optimize uses of this class. That's
    why they come up with an idea to cache all String instances created
    inside double quotes e.g. "Java". These double quoted literal is
    known as String literal and the cache which stored these String
    instances are known as as String pool.

    At high level both are String object, but main difference comes
    from the point that new() operator always creates a new String
    object. Also when you create String using literal they are interned.

    1
    2
    3
    String a ="Java";
    String b ="Java";
    System.out.println(a == b);  // True

    Here two different objects are created and they have different
    references:

    1
    2
    3
    String c = new String("Java");
    String d = new String("Java");
    System.out.println(c == d);  // False

    Similarly when you compare a String literal with an String object
    created using new() operator using == operator, it will return false,
    as shown below :

    1
    2
    3
    String e ="JDK";
    String f =  new String("JDK");
    System.out.println(e == f);  // False

    号垃圾收集器

    参考资料在这里。

    In fact the String objects that correspond to String literals
    typically are not candidates for garbage collection. This is because
    there is an implicit reference to the string object in the code of
    every method that uses the literal. This means that the String is
    reachable for as long as the method could be executed.

    However, this is not always the case. If the literal was defined in a
    class that was dynamically loaded (e.g. using Class.forName(...)),
    then it is possible to arrange that the class is unloaded. If that
    happens, then the String object for the literal will be unreachable,
    and will be reclaimed when the heap containing the interned String
    gets GC'ed.

    号字符串池

    参考文献在这里。

    java.lang.String.intern() returns an interned String, that is, one
    that has an entry in the global String pool. If the String is not
    already in the global String pool, then it will be added.

    通过编程,您可以遵循以下方法:

  • 因此,对于任意两个字符串st来说,s.intern() == t.intern()是真的,前提是s.equals(t)是真的。
  • 因此,如果在字符串上使用intern()

  • 打电话给String.intern()
  • 然后:

  • 保证来自唯一字符串池。